This tutorial walks you through a production-realistic OIDC federation setup using Okta.
You’ll create an Okta OIDC app integration, implement Authorization Code + PKCE, validate an ID token, and protect an authenticated route.
What you’ll build
- A minimal web app that redirects to Okta for login
- A callback handler that exchanges the code for tokens (with PKCE)
- ID token validation (issuer, audience, expiry, nonce)
- A protected page that shows the authenticated subject + claims
Prerequisites
- Okta developer org
- Node.js 20+ (22 is fine)
- Basic comfort with HTTP redirects
Architecture overview
Diagram
Step 1 — Create the Okta OIDC app integration
-
In Okta Admin Console, create an OIDC application integration.
-
Choose:
- Web Application
- Grant type: Authorization Code
- (If available) require PKCE
- Configure redirect URIs:
http://localhost:3000/callback
- Copy these values:
- Issuer (or Okta domain + authorization server)
- Client ID
- Client Secret (if you’re using a confidential client)
Step 2 — Create the demo app (skeleton)
You can implement this tutorial in whatever stack you prefer. The key parts are the same:
/loginendpoint that builds an authorization request/callbackendpoint that exchangescodefor tokens/meendpoint that requires a session
At minimum, store:
code_verifier(server-side)stateandnonce(server-side)- a server session cookie for the user
Step 3 — Generate PKCE + redirect to Okta
When the user clicks Login:
- generate
code_verifier(random) - derive
code_challenge(S256) - generate
state(CSRF) - generate
nonce(bind to ID token)
Then redirect to Okta’s authorize endpoint.
Common pitfall: if you don’t validate state, you’re vulnerable to login CSRF.
Step 4 — Handle callback and exchange code for tokens
On the callback:
- validate
state - send token request with:
grant_type=authorization_codecoderedirect_uricode_verifier
Okta returns (typically):
id_tokenaccess_token- optionally
refresh_token
Step 5 — Validate the ID token (minimum checks)
At minimum validate:
- signature (fetch JWKs via discovery)
issmatches your issueraudcontains your client IDexpnot expired (account for clock skew)noncematches
Common pitfall: only decoding the JWT and trusting its contents.
Step 6 — Create a session and protect a route
Once ID token validation passes:
- create a server session record
- set an HttpOnly cookie
- protect your
/meroute by checking the session
Troubleshooting
- redirect_uri mismatch: exact string mismatch between app and Okta config
- invalid_client: wrong client auth method (secret vs none)
- invalid_grant: code used twice, wrong code_verifier, or wrong redirect_uri
- JWK fetch failures: wrong issuer / network
Hardening / production notes
- Prefer PKCE everywhere (including public clients)
- Use short-lived access tokens + refresh token rotation where appropriate
- Be explicit about scopes and claims
- Log auth failures with correlation IDs (but don’t log tokens)
Cleanup
- Delete the Okta app integration when you’re done (or keep it for future labs).
Where to go next
- Specs: /category/specifications
- OIDC deep dive: /topic/specifications/openid-connect-oidc-identity-layer-on-oauth
- OAuth hardening: /topic/specifications/oauth-2-1-best-practices-pkce-rotation-threat-model