Federated login and passkeys

Use authorization code with PKCE

Start a modern authorization flow with state and PKCE, validate the callback, and exchange a one-time code without exposing tokens in URLs.

The authorization code flow sends the browser back with a short-lived code. PKCE binds that code to the client instance that began the flow.

Generate a secure state value and PKCE verifier, store them in a short-lived transaction, send the derived challenge, and validate state exactly on callback. Use exact registered redirect URIs and exchange the code server-side where the application architecture supports it.

The browser-visible request contains the challenge, not the verifier:

sequenceDiagram
  accTitle: Authorization code flow with PKCE
  accDescr: The client stores a verifier, sends only its challenge through the browser, receives a code, and proves possession of the verifier during the token exchange.
  participant Client
  participant Browser
  participant Provider

  Client->>Client: Create verifier and challenge
  Client->>Browser: Store transaction and redirect
  Browser->>Provider: Authorization request with challenge
  Provider-->>Browser: Redirect with code and state
  Browser->>Client: Callback with code and state
  Client->>Provider: Code plus original verifier
  Provider-->>Client: Access token

After the callback, the client sends the original verifier to the token endpoint. A party that steals only the authorization code cannot complete the exchange without it.

state and PKCE protect different links. state ties the callback to the browser transaction and helps prevent login CSRF. PKCE ties the code to the client that started the flow. Use both according to the provider and library guidance.

Store the transaction server-side or in a protected, short-lived mechanism. Bind it to the authorization server that was selected, and include the intended post-login destination after validating it as a safe local path. A client that supports several issuers must validate the callback issuer, or use distinct redirect URIs, so a response from one provider cannot be confused with a transaction started at another. Never accept an arbitrary callback returnTo URL and redirect to it.

Consume the transaction once. A second callback with the same state, code, or verifier should fail even inside the expiry window.

Implement a fake provider flow in tests. Cover wrong state, wrong verifier, wrong issuer, reused code, expired transaction, and an external return URL.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →