Skip to main content

OAuth 2.0 with PKCE

Understand the OAuth 2.0 Authorization Code flow with PKCE (Proof Key for Code Exchange).


What is PKCE?

PKCE (pronounced "pixy") is a security extension to OAuth 2.0 that protects against authorization code interception attacks.

Why PKCE?

Without PKCE, if an attacker intercepts the authorization code, they could:

  • Exchange it for tokens
  • Gain access to user accounts

PKCE prevents this by requiring proof that the same client that initiated the flow is completing it.


How PKCE Works

1. Generate Code Verifier & Challenge

The client must first create a high-entropy random string (verifier) and its hash (challenge).

// 1. Generate Code Verifier (Random 43-128 chars)
const generateRandomString = (length) => {
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
let text = '';
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};
const codeVerifier = generateRandomString(64);

// 2. Generate Code Challenge (SHA-256 of verifier)
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await window.crypto.subtle.digest('SHA-256', data);
const base64Digest = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
return base64Digest;
}

3. Authorization Request

GET /authorize?
response_type=code
&client_id=your-client-id
&redirect_uri=https://yourapp.com/callback
&scope=openid profile email
&state=random-state-value
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256

4. Token Exchange

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=received-auth-code
&redirect_uri=https://yourapp.com/callback
&client_id=your-client-id
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

The server verifies that sha256(code_verifier) == code_challenge before issuing tokens.


Complete Flow Diagram


SyAuth Server-Side PKCE

SyAuth also supports server-side PKCE sessions to solve cross-origin issues in certain architectures.

Initialize PKCE Session

POST /oauth/pkce/init
Content-Type: application/json

{}

Response:

{
"session_id": "pkce_session_xxx",
"code_challenge": "ABC123...",
"code_challenge_method": "S256"
}

Use in Token Exchange

POST /oauth/token
{
"grant_type": "authorization_code",
"code": "xxx",
"pkce_session_id": "pkce_session_xxx" // Instead of code_verifier
}

SDK Handling

The SyAuth SDK handles all PKCE complexity automatically:

import { useSyAuth } from '@syauth/nextjs';

function LoginButton() {
const { loginWithRedirect } = useSyAuth();

// SDK automatically:
// 1. Generates code_verifier
// 2. Calculates code_challenge
// 3. Stores verifier in secure cookie
// 4. Includes challenge in auth request
// 5. Retrieves verifier for token exchange

return (
<button onClick={() => loginWithRedirect()}>
Login
</button>
);
}

Security Best Practices

PracticeDescription
Use S256 methodSHA-256 is more secure than plain
Secure verifier storageUse HttpOnly cookies, not localStorage
Validate state parameterPrevent CSRF attacks
Use short-lived auth codesCodes expire in 5 minutes

Common Issues

"Invalid code_verifier"

The verifier doesn't match the challenge. Ensure you're using the exact same verifier that was used to create the challenge.

"Code already used"

Authorization codes are one-time use. Request a new one if needed.

"Code expired"

Authorization codes expire after 5 minutes. Restart the flow.


Next Steps