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