Skip to content

🔒 PKCE Implementation Guide

PKCE (Proof Key for Code Exchange) is required for all OAuth flows with Earth Miles. This guide shows you how to implement it correctly.

What is PKCE?

PKCE enhances OAuth security by preventing authorization code interception attacks. It works by:

  1. Generating a random code_verifier
  2. Creating a code_challenge by hashing the verifier
  3. Sending the challenge with the authorization request
  4. Sending the original verifier with the token exchange

Implementation Steps

1. Generate Code Verifier

Create a cryptographically random string of 43-128 characters:

import base64
import secrets

def generate_code_verifier():
    """Generate a random code verifier."""
    return base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')
const crypto = require('crypto');

function generateCodeVerifier() {
    return crypto.randomBytes(32).toString('base64url');
}

2. Create Code Challenge

Hash the verifier with SHA-256 and Base64URL encode it:

import hashlib
import base64

def generate_code_challenge(code_verifier):
    """Generate code challenge from verifier."""
    digest = hashlib.sha256(code_verifier.encode('utf-8')).digest()
    return base64.urlsafe_b64encode(digest).decode('utf-8').rstrip('=')
const crypto = require('crypto');

function generateCodeChallenge(codeVerifier) {
    return crypto.createHash('sha256').update(codeVerifier).digest('base64url');
}

Full Example

import base64
import hashlib
import secrets
import urllib.parse

def generate_code_verifier():
    """Generate a random code verifier."""
    return base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')

def generate_code_challenge(code_verifier):
    """Generate code challenge from verifier."""
    digest = hashlib.sha256(code_verifier.encode('utf-8')).digest()
    return base64.urlsafe_b64encode(digest).decode('utf-8').rstrip('=')

# Example usage
code_verifier = generate_code_verifier()
code_challenge = generate_code_challenge(code_verifier)

# Build authorization URL
params = {
    'client_id': 'your_client_id',
    'redirect_uri': 'https://your-app.com/callback',
    'scope': 'miles:read miles:write',
    'state': secrets.token_urlsafe(16),
    'response_type': 'code',
    'code_challenge': code_challenge,
    'code_challenge_method': 'S256'
}

auth_url = f"https://backend.earthmiles.app/oauth/authorize?{urllib.parse.urlencode(params)}"
const crypto = require('crypto');

function generateCodeVerifier() {
    return crypto.randomBytes(32).toString('base64url');
}

function generateCodeChallenge(codeVerifier) {
    return crypto.createHash('sha256').update(codeVerifier).digest('base64url');
}

// Example usage
const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);

console.log('Code Verifier:', codeVerifier);
console.log('Code Challenge:', codeChallenge);

Security Best Practices

  • Generate truly random verifiers using cryptographically secure random number generators
  • Store code verifiers securely - preferably server-side, never in client-side storage (avoid localStorage or cookies)
  • Use the verifier only once - generate new PKCE parameters for each authorization flow
  • Validate parameters - ensure code challenge matches the verifier during token exchange
  • Implement proper error handling for PKCE validation failures