-
-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Description
Implement PKCE (Proof Key for Code Exchange) support as defined in RFC 7636. PKCE is a security extension to OAuth 2.0 that protects against authorization code interception attacks, especially important for public clients like CLI tools and desktop applications.
What needs to be done
-
Generate PKCE parameters:
- Create cryptographically secure
code_verifier(43-128 characters) - Generate
code_challengefrom verifier using SHA256 - Support both
plainandS256challenge methods
- Create cryptographically secure
-
Modify authorization flow:
- Add
code_challengeandcode_challenge_methodto authorization URL - Store
code_verifierfor token exchange - Include
code_verifierin token exchange request
- Add
-
API design:
- Make PKCE optional but recommended
- Auto-enable for public clients
- Allow manual override
Why this matters
PKCE is becoming mandatory:
- OAuth 2.1 requires PKCE for all clients
- Many providers (GitHub, Google) already recommend or require it
- Prevents authorization code interception attacks
- Essential for security of CLI and desktop applications
Implementation considerations
-
Backward compatibility: How do we add PKCE without breaking existing users? Should it be opt-in or opt-out?
-
Storage challenge: Where do we store the code_verifier between authorization and token exchange? In memory? File system?
-
Alternative approach: Should we create a higher-level
OAuthClientclass that handles the complete flow including PKCE, rather than adding to the existing function? -
Crypto dependencies: Node, Deno, and Bun have different crypto APIs. How do we handle this elegantly?
-
Challenge method negotiation: Some servers only support
plain, others requireS256. Should we auto-detect?
Suggested API design
// Option 1: Add to existing options
const result = await getAuthCode({
authorizationUrl: "https://oauth.example.com/authorize",
pkce: true, // or 'S256' | 'plain' | false
// Returns code AND code_verifier
});
// Option 2: Separate PKCE utilities
import { generatePKCE } from 'oauth-callback/pkce';
const pkce = await generatePKCE();
const authUrl = new URL("https://oauth.example.com/authorize");
authUrl.searchParams.set("code_challenge", pkce.challenge);
authUrl.searchParams.set("code_challenge_method", "S256");
const result = await getAuthCode(authUrl.toString());
// Use pkce.verifier in token exchange
// Option 3: High-level client
const client = new OAuthClient({
clientId: "...",
authorizationUrl: "...",
tokenUrl: "...",
pkce: true
});
const tokens = await client.authorize();Implementation example
// src/pkce.ts
export async function generatePKCE(method: 'S256' | 'plain' = 'S256') {
// Generate random verifier
const buffer = new Uint8Array(32);
crypto.getRandomValues(buffer);
const verifier = base64url(buffer);
// Generate challenge
let challenge: string;
if (method === 'S256') {
const hash = await crypto.subtle.digest('SHA-256',
new TextEncoder().encode(verifier)
);
challenge = base64url(new Uint8Array(hash));
} else {
challenge = verifier;
}
return {
verifier,
challenge,
method
};
}
function base64url(buffer: Uint8Array): string {
// Implementation...
}Testing requirements
- Verify verifier length (43-128 chars)
- Test challenge generation with both methods
- Ensure cryptographic randomness
- Test with real OAuth providers that support PKCE
- Verify error handling when PKCE is required but not provided
References
Skills required
- TypeScript
- OAuth 2.0 and PKCE specification
- Cryptography (SHA256, base64url encoding)
- Cross-platform JavaScript (Node/Deno/Bun)
Difficulty
Medium - Requires understanding of OAuth security and cryptography