Quick Answer
AI-generated OAuth implementations consistently make three mistakes: missing the state parameter (enabling CSRF attacks on login), not implementing PKCE (allowing authorization code interception), and using wildcard or overly broad redirect URIs (enabling token theft via open redirects). These are not edge cases - they are the default output of most AI coding tools.
Why OAuth Is Hard for AI to Get Right
OAuth 2.0 is a complex protocol with multiple flows, grant types, and security parameters that interact in non-obvious ways. AI coding tools learn from millions of code examples, but most OAuth tutorials online demonstrate the happy path without security parameters. The result: AI generates "working" OAuth flows that are vulnerable to well-known attacks.
According to a 2023 study published at the IEEE Symposium on Security and Privacy, 61% of OAuth implementations in popular web frameworks had at least one exploitable vulnerability. The most common issues were missing state parameters and insufficient redirect URI validation - exactly what AI tools generate.
The OAuth 2.0 Security Best Current Practice RFC (RFC 9700) explicitly requires PKCE for all OAuth clients, yet AI-generated code almost never includes it. OWASP lists broken authentication as the #2 web application security risk.
Mistake 1: Missing State Parameter
The state parameter prevents CSRF attacks on the OAuth callback. Without it, an attacker can trick a logged-in user into linking the attacker's account.
// ❌ BAD - No state parameter (CSRF vulnerable)
const authUrl = `https://github.com/login/oauth/authorize?` +
`client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=read:user`;
// Attacker can craft this URL with their own authorization code
// ✅ GOOD - Generate and verify state parameter
import crypto from 'crypto';
// On login initiation
const state = crypto.randomBytes(32).toString('hex');
req.session.oauthState = state;
const authUrl = `https://github.com/login/oauth/authorize?` +
`client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}` +
`&scope=read:user&state=${state}`;
// On callback
app.get('/auth/callback', (req, res) => {
if (req.query.state !== req.session.oauthState) {
return res.status(403).json({ error: 'Invalid state parameter' });
}
delete req.session.oauthState;
// ... exchange code for token
});
Mistake 2: No PKCE Implementation
PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks. Without PKCE, a malicious app or browser extension can intercept the authorization code and exchange it for an access token.
// ✅ GOOD - PKCE implementation
import crypto from 'crypto';
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url');
return { verifier, challenge };
}
// On login initiation
const { verifier, challenge } = generatePKCE();
req.session.codeVerifier = verifier;
const authUrl = `https://provider.com/authorize?` +
`client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}` +
`&code_challenge=${challenge}&code_challenge_method=S256`;
// On callback - include verifier in token exchange
const tokenResponse = await fetch('https://provider.com/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'authorization_code',
code: req.query.code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
code_verifier: req.session.codeVerifier,
}),
});
Mistake 3: Open Redirect URIs
AI-generated OAuth configurations often use wildcard or overly broad redirect URI patterns, allowing attackers to redirect tokens to their own servers.
// ❌ BAD - Accepting any redirect URI
const allowedRedirects = [/https:\/\/.*\.myapp\.com/];
// Attacker creates: https://evil.myapp.com.attacker.com
// ❌ BAD - Matching prefix only
if (redirectUri.startsWith('https://myapp.com')) { ... }
// Attacker uses: https://myapp.com.attacker.com/steal-token
// ✅ GOOD - Exact match on full redirect URI
const ALLOWED_REDIRECTS = new Set([
'https://myapp.com/auth/callback',
'https://staging.myapp.com/auth/callback',
]);
if (!ALLOWED_REDIRECTS.has(redirectUri)) {
return res.status(400).json({ error: 'Invalid redirect URI' });
}
| OAuth Mistake | Attack Enabled | Impact |
|---|---|---|
| Missing state parameter | Login CSRF | Account linking to attacker's identity |
| No PKCE | Authorization code interception | Full account takeover |
| Open redirect URIs | Token theft via redirect | Access token stolen by attacker |
| Token in URL fragment | Referrer header leakage | Token leaked to third-party sites |
| No token expiry check | Stale token reuse | Indefinite unauthorized access |
How to Fix Your OAuth Implementation
- Add the state parameter to every OAuth flow. Generate it with
crypto.randomBytes(32)and verify it on callback. - Implement PKCE for all public clients (SPAs, mobile apps, desktop apps).
- Use exact redirect URI matching. Never use wildcards, prefix matching, or regex for redirect validation.
- Use a proven library instead of hand-coding OAuth. NextAuth.js, Passport.js, and arctic handle these security parameters automatically.
- Scan your codebase. Tools like VibeDoctor (vibedoctor.io) automatically detect missing CSRF protection and insecure authentication patterns in your API routes. Free to sign up.
FAQ
Should I implement OAuth myself or use a library?
Use a library. OAuth has too many security-critical details for hand implementation. NextAuth.js (for Next.js), Passport.js (for Express), and arctic (lightweight) all handle state, PKCE, and redirect validation correctly. The time you save avoiding security bugs far outweighs the learning curve.
Is PKCE required for server-side apps?
RFC 9700 (OAuth 2.0 Security Best Practices) recommends PKCE for all OAuth clients, including server-side applications. While the authorization code interception risk is lower for confidential clients, PKCE adds defense-in-depth and is simple to implement.
What is the difference between OAuth 2.0 and OpenID Connect?
OAuth 2.0 is an authorization protocol (granting access to resources). OpenID Connect (OIDC) is an authentication layer built on top of OAuth 2.0 (verifying identity). If you need "Login with Google/GitHub", you want OIDC. If you need "access the user's GitHub repos", you want OAuth 2.0. Most AI-generated "OAuth" code is actually using OIDC flows.
Does NextAuth handle PKCE automatically?
Yes. NextAuth.js v5 (Auth.js) implements PKCE by default for all OAuth providers that support it. It also handles state parameters, CSRF tokens, and redirect URI validation automatically. This is one of the strongest arguments for using a library instead of implementing OAuth from scratch.