Skip to main content

PKCE (Proof Key for Code Exchange)

Understanding PKCE and how it enhances OAuth security.

What is PKCE?​

PKCE (Proof Key for Code Exchange) is a security extension to OAuth 2.0 that prevents authorization code interception attacks. It's especially important for public clients (mobile apps, SPAs) but is also recommended for confidential clients.

How PKCE Works​

Step 1: Generate Code Verifier​

A random, cryptographically random string is generated:

import secrets
import base64

code_verifier = base64.urlsafe_b64encode(
secrets.token_bytes(32)
).decode('utf-8').rstrip('=')

Step 2: Generate Code Challenge​

The code verifier is hashed using SHA256:

import hashlib

code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode('utf-8')).digest()
).decode('utf-8').rstrip('=')

Step 3: Authorization Request​

The code challenge is sent in the authorization request:

GET /oauth/authorize?
client_id=...
&redirect_uri=...
&code_challenge={code_challenge}
&code_challenge_method=S256

Step 4: Token Exchange​

The code verifier is sent in the token exchange:

POST /oauth/token
{
"grant_type": "authorization_code",
"code": "...",
"code_verifier": "{code_verifier}",
...
}

Step 5: Verification​

The provider verifies:

computed_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode('utf-8')).digest()
).decode('utf-8').rstrip('=')

if computed_challenge != stored_challenge:
raise InvalidCodeVerifier()

Automatic PKCE​

The sva-oauth-client package automatically implements PKCE:

from sva_oauth_client.client import SVAOAuthClient

client = SVAOAuthClient(...)

# PKCE is automatically handled
auth_url, code_verifier = client.get_authorization_url()
# code_verifier is stored in session
# code_challenge is included in auth_url

Security Benefits​

Prevents Code Interception​

Without PKCE:

  1. Attacker intercepts authorization code
  2. Attacker exchanges code for tokens
  3. Attacker gains access

With PKCE:

  1. Attacker intercepts authorization code
  2. Attacker doesn't have code verifier
  3. Attacker cannot exchange code
  4. Attack fails

Protects Public Clients​

PKCE is especially important for:

  • Mobile applications
  • Single-page applications (SPAs)
  • Desktop applications

Defense in Depth​

Even for confidential clients, PKCE provides:

  • Additional security layer
  • Protection against compromised secrets
  • Better security posture

Implementation Details​

Code Verifier Requirements​

  • Length: 43-128 characters
  • Character set: A-Z, a-z, 0-9, -, ., _, ~
  • Randomness: Cryptographically random

Code Challenge Method​

SVA OAuth supports:

  • S256 - SHA256 (recommended)
  • plain - Plain text (not recommended)

Storage​

The code verifier is stored:

  • In session (server-side)
  • Temporarily in localStorage (client-side, for callback)
  • Never exposed to user

Best Practices​

  1. Always use PKCE - Even for confidential clients
  2. Use S256 - SHA256 method is more secure
  3. Store securely - Code verifier in server-side session
  4. Clear after use - Remove code verifier after token exchange
  5. Don't reuse - Generate new verifier for each flow

Verification​

Manual Verification​

You can verify PKCE is working:

from sva_oauth_client.client import SVAOAuthClient

client = SVAOAuthClient(...)
auth_url, code_verifier = client.get_authorization_url()

# Check auth_url contains code_challenge
assert 'code_challenge' in auth_url
assert 'code_challenge_method=S256' in auth_url

# Verify code_verifier is generated
assert code_verifier is not None
assert len(code_verifier) >= 43

Next Steps​