advanced

JWT, OAuth, OpenID Connect, and SSO

Separate token formats from auth protocols, validate issuer/audience/expiry/signature, and integrate OAuth/OIDC/SSO without trusting client claims blindly.

JWT is a signed (or encrypted) token format — not an auth protocol by itself. OAuth 2.0 delegates authorization; OpenID Connect adds identity layers (ID token, UserInfo) on top. SSO uses a central IdP so users authenticate once across apps.

					import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(new URL('https://idp.example.com/.well-known/jwks.json'));

async function authenticateBearer(req) {
  const token = req.headers.authorization?.replace(/^Bearer /, '');
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: 'https://idp.example.com',
    audience: 'api.example.com',
  });
  return payload;
}
				

Always validate `iss`, `aud`, `exp`, and signature — never trust client-decoded claims. Prefer short-lived access tokens plus refresh tokens stored securely. For opaque tokens, use introspection at the authorization server.

On interviews: OAuth flows (authorization code with PKCE for SPAs); difference between access and ID tokens; why JWT logout is hard without blocklists or short TTL.

Common pitfalls: accepting `alg: none`; storing refresh tokens in localStorage; confusing OAuth scopes with application permissions.

The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.

Checklist:

  • Verify signature, issuer, audience, expiry.
  • Use authorization code + PKCE for public clients.
  • Short-lived access tokens; secure refresh handling.
  • Map external identity to internal authorization separately.