Skip to content

Add support for PKCE (Proof Key for Code Exchange) #7

@koistya

Description

@koistya

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

  1. Generate PKCE parameters:

    • Create cryptographically secure code_verifier (43-128 characters)
    • Generate code_challenge from verifier using SHA256
    • Support both plain and S256 challenge methods
  2. Modify authorization flow:

    • Add code_challenge and code_challenge_method to authorization URL
    • Store code_verifier for token exchange
    • Include code_verifier in token exchange request
  3. 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

⚠️ Note: This feature requires critical thinking during implementation. Consider:

  1. Backward compatibility: How do we add PKCE without breaking existing users? Should it be opt-in or opt-out?

  2. Storage challenge: Where do we store the code_verifier between authorization and token exchange? In memory? File system?

  3. Alternative approach: Should we create a higher-level OAuthClient class that handles the complete flow including PKCE, rather than adding to the existing function?

  4. Crypto dependencies: Node, Deno, and Bun have different crypto APIs. How do we handle this elegantly?

  5. Challenge method negotiation: Some servers only support plain, others require S256. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions