Skip to content

OAuth 2.0 PKCE — Complete Mobile and SPA Auth Guide

DodaTech Updated 2026-06-28 2 min read

In this tutorial, you will learn about OAuth 2.0 PKCE. We cover key concepts, practical examples, and best practices to help you master this topic.

PKCE (Proof Key for Code Exchange) is an extension to the OAuth 2.0 authorization code flow designed for public clients (mobile apps and SPAs) that cannot securely store a client_secret.

What You'll Learn

You'll learn how PKCE works, how to generate the code challenge and verifier, and how to implement it in mobile and browser applications.

Why It Matters

Mobile apps and SPAs cannot keep client secrets secret. PKCE eliminates the need for a client_secret by using a dynamically generated cryptographic key that is never stored.

Real-World Use

A React SPA uses PKCE to authenticate users with Auth0. The app generates a code verifier, creates a code challenge, and exchanges the authorization code using the verifier. No client_secret is ever stored in the browser.

sequenceDiagram
    participant App as SPA/Mobile App
    participant Auth as Authorization Server
    App->>App: Generate code_verifier (random string)
    App->>App: code_challenge = SHA256(code_verifier)
    App->>Auth: Authorize (code_challenge, method=S256)
    Auth->>App: Authorization code
    App->>Auth: POST /token (code, code_verifier)
    Auth->>Auth: Verify SHA256(verifier) == challenge
    Auth->>App: access_token + refresh_token

Teacher's Mindset

PKCE is like a self-destructing message. The client sends a locked box (code challenge) with the auth request. When exchanging the code, it sends the key (code verifier). The server verifies the key opens the box, and both the key and box are discarded.

Implementation

import hashlib
import base64
import secrets
import requests

def generate_pkce_pair():
    code_verifier = secrets.token_urlsafe(64)[:128]
    code_challenge = base64.urlsafe_b64encode(
        hashlib.sha256(code_verifier.encode()).digest()
    ).rstrip("=").decode()
    return code_verifier, code_challenge

def create_auth_url(client_id, redirect_uri, code_challenge):
    params = {
        "response_type": "code",
        "client_id": client_id,
        "redirect_uri": redirect_uri,
        "code_challenge": code_challenge,
        "code_challenge_method": "S256",
        "scope": "openid profile",
        "state": secrets.token_urlsafe(32)
    }
    return f"https://auth.example.com/authorize?{urllib.parse.urlencode(params)}"
def exchange_code_for_token(auth_code, code_verifier, client_id, redirect_uri):
    response = requests.post(
        "https://auth.example.com/token",
        data={
            "grant_type": "authorization_code",
            "code": auth_code,
            "client_id": client_id,
            "redirect_uri": redirect_uri,
            "code_verifier": code_verifier
        },
        headers={"Content-Type": "application/x-www-form-urlencoded"}
    )
    return response.json()

# Usage
verifier, challenge = generate_pkce_pair()
print(f"Verifier: {verifier[:20]}...")
print(f"Challenge: {challenge[:20]}...")

Common Mistakes

Mistake Fix
Using "plain" code challenge method S256 is more secure; plain sends verifier in auth request
Incorrect base64 encoding Use URL-safe base64 without padding
Verifier too short Minimum 43 characters, maximum 128
Not using PKCE for public clients Without PKCE, public clients need secret they cannot protect
Reusing code verifier Generate new verifier for each auth request

Practice Questions

  1. Why is PKCE needed for mobile apps?
  2. How does the code challenge prevent interception attacks?
  3. What is the difference between S256 and plain methods?
  4. Why can't SPAs use a client_secret?
  5. Does PKCE replace the state parameter?

Challenge

Implement the full PKCE flow with a mock authorization server. The client generates a verifier/challenge, requests authorization, receives the code, and exchanges it using the verifier. Test that invalid verifiers are rejected.

What's Next

Learn about Openid Connect for user authentication on top of OAuth 2.0.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro