0Pricing
Frontend Academy · Lesson

OAuth Flows from the Frontend

Implement the Authorization Code flow with PKCE in a SPA, exchange the code for tokens, store access tokens safely, and avoid implicit flow.

OAuth Flows from the Frontend is a free Frontend Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is OAuth?

OAuth 2.0 is a delegation protocol: your app gets permission to act on the user's behalf at a third-party service (Google, GitHub, etc.) without ever seeing the user's password. Used for 'Sign in with Google', GitHub OAuth apps, and most modern auth.

Key Roles

Resource Owner: the user. Client: your app. Authorization Server: the OAuth provider (Google, Auth0). Resource Server: the API protected by the access token.

The Authorization Code Flow

The standard, secure flow for server-side apps: 1) Redirect user to provider's login. 2) User logs in and consents. 3) Provider redirects back with a code. 4) Server exchanges code for access token (server-only secret).

Why PKCE for SPAs

SPAs can't keep a client secret (it'd be in the browser bundle). PKCE (Proof Key for Code Exchange) replaces the secret with a per-request code verifier/challenge. Now standard for all OAuth public clients.

The PKCE Flow Step-by-Step

1) Generate a random code_verifier. 2) SHA-256 hash it = code_challenge. 3) Redirect to /authorize with the challenge. 4) After consent, get a code back. 5) Exchange code + verifier for access token. The provider verifies challenge = hash(verifier).

Generating the Code Verifier and Challenge

The verifier is a random string; the challenge is its SHA-256 base64-URL-encoded.

function base64url(arr) {
  return btoa(String.fromCharCode(...arr))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

async function generatePKCE() {
  const arr = new Uint8Array(32);
  crypto.getRandomValues(arr);
  const verifier = base64url(arr);
  const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
  const challenge = base64url(new Uint8Array(hash));
  return { verifier, challenge };
}

Redirecting to the Provider

Build the authorize URL with the challenge, state (CSRF protection), and scopes.

const { verifier, challenge } = await generatePKCE();
const state = crypto.randomUUID();
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('oauth_state', state);

const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
url.searchParams.set('client_id', CLIENT_ID);
url.searchParams.set('redirect_uri', `${origin}/auth/callback`);
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email profile');
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
url.searchParams.set('state', state);

window.location.href = url.toString();

Handling the Callback

The provider redirects to your callback URL with ?code=...&state=.... Verify state to prevent CSRF, then exchange the code.

// /auth/callback page:
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const returnedState = params.get('state');
const expected = sessionStorage.getItem('oauth_state');
if (returnedState !== expected) throw new Error('Bad state');

const verifier = sessionStorage.getItem('pkce_verifier');

const tokens = await fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code,
    client_id: CLIENT_ID,
    redirect_uri: `${origin}/auth/callback`,
    code_verifier: verifier
  })
}).then(r => r.json());
// tokens: { access_token, id_token, refresh_token, expires_in }

Storing Tokens Safely

Best practice: don't store access tokens in localStorage (XSS-stealable). Options: 1) In memory only (cleared on refresh — re-auth needed). 2) HttpOnly cookie (set by your backend after PKCE flow, not stealable by JS).

Avoid the Implicit Flow

The old implicit flow returns the access token directly in the URL fragment. Deprecated by OAuth 2.0 Security BCP — leaks tokens to browser history, referrers. Use Authorization Code + PKCE always.

Refresh Tokens

Access tokens expire (typically 1 hour). A refresh token (longer-lived) gets new access tokens silently. For SPAs, refresh tokens are increasingly delivered via HttpOnly cookies — never localStorage.

ID Tokens vs Access Tokens

ID token (OpenID Connect): JWT proving who the user is. Access token: opaque or JWT, used to call APIs. Verify the ID token's signature and claims (iss, aud, exp, nonce) before trusting.

Auth Libraries

Don't roll your own. Use: oidc-client-ts for raw OIDC, auth0/spa-js for Auth0, @clerk/clerk-react for Clerk, NextAuth.js/Auth.js for Next, Nuxt-auth for Nuxt. They handle PKCE, refresh, and storage.

Quick Check

Why must Single-Page Applications use PKCE (Proof Key for Code Exchange) with the Authorization Code flow instead of just the basic Authorization Code flow?

Recap: OAuth in the Frontend

Authorization Code + PKCE is the modern standard for SPAs. Generate verifier + S256 challenge per request. Redirect to /authorize with challenge + state. Verify state on callback. Exchange code + verifier for tokens. Never use Implicit flow. Prefer HttpOnly cookies for token storage. Use libraries (auth0-spa-js, oidc-client-ts, NextAuth) — don't roll your own.

Frequently asked questions

Is the “OAuth Flows from the Frontend” lesson free?

Yes — the full text of “OAuth Flows from the Frontend” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.

What will I learn in “OAuth Flows from the Frontend”?

Implement the Authorization Code flow with PKCE in a SPA, exchange the code for tokens, store access tokens safely, and avoid implicit flow. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “OAuth Flows from the Frontend” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. XSS Prevention: Output Encoding CSP
  2. CSRF: SameSite Cookies and Tokens
  3. Content Security Policy: nonce and hash
  4. OAuth Flows from the Frontend
← Back to Frontend Academy