On this page

OAuth2 and OpenID Connect

OAuth2 is an authorization framework (allowing apps to access your resources on your behalf), while OIDC adds a layer of authentication on top (allowing apps to verify your identity). Authorization code + PKCE is the current best practice; the implicit flow is dead.

Overview

OAuth2 (RFC 6749, 2012) is the standard framework for internet authorization—"allowing third-party apps to access my resources without needing my password." The core flow is Authorization Code + PKCE: the browser redirects to the authorization server → the user logs in → the browser returns with a code → the client exchanges the code for an access token. OIDC (OpenID Connect, 2014) adds identity authentication (id_token) on top of OAuth2, becoming the foundation for "Login with Google/GitHub." JWT is the most common format for OAuth2 access tokens—self-contained, verifiable, and stateless.

OAuth2: Authorization (Not Authentication)

Four roles:
  Resource Owner: The user
  Client: Third-party application
  Authorization Server: Authorization server
  Resource Server: API server

Authorization Code + PKCE (the only recommended web flow):
  1. Client: Generate code_verifier (random), code_challenge = SHA256(verifier)
  2. Browser redirect:
     GET /authorize?response_type=code
       &client_id=grafana
       &redirect_uri=https://grafana.example.com/login/callback
       &scope=openid profile email
       &state=random123
       &code_challenge=<base64url(challenge)>
       &code_challenge_method=S256
  3. User logs in + consents
  4. Browser redirects back: https://grafana.example.com/login/callback?code=AUTH_CODE&state=random123
  5. Client → Authorization Server (backchannel):
     POST /token
     grant_type=authorization_code
     &code=AUTH_CODE
     &redirect_uri=...
     &code_verifier=<verifier>  ← server verifies SHA256(verifier)==challenge
  6. Response: { "access_token", "refresh_token", "id_token", "token_type":"Bearer", "expires_in":3600 }
  7. Client: Requests API using access_token (Authorization: Bearer <token>)

Why PKCE is Needed

In traditional flows, the code can be intercepted (the redirect URI for a mobile app might be registered by another app). PKCE ensures that even if the code is leaked, the token cannot be exchanged without the code_verifier.

JWT

Format: base64(header).base64(payload).signature

Header: {"alg":"RS256","typ":"JWT","kid":"key-2025"}
Payload (claims):
  iss: Issuer
  sub: User ID (subject)
  aud: Audience (who this token is for)
  exp: Expiration time (unix)
  iat: Issued at time
  email, name, groups: Custom claims

Signature: RSA-SHA256(base64(header)+"."+base64(payload))

Verification:
  1. Fetch JWKS (https://auth.example.com/.well-known/jwks.json) → get the public key corresponding to the kid
  2. Verify the signature
  3. Check exp (not expired), aud (this token is for me), iss (from a trusted issuer)

OpenID Connect (OIDC)

OIDC = OAuth2 + User Identity (id_token):

id_token (JWT): Sent to the client to prove "who the user is" (not for calling APIs)
access_token:   Sent to the API to prove "the client has permission to access" (does not contain user info)

Client Credentials (Machine-to-Machine)

POST /token
  grant_type=client_credentials
  &client_id=my-app
  &client_secret=secret

→ { "access_token":"...", "expires_in":3600 }
→ No user involvement

References

  • jwt.io: Debug JWTs
  • Keycloak: Open source identity provider
  • OIDC playground: openidconnect.net

Keywords: OAuth2, PKCE, JWT, OIDC, id_token, client credentials, JWKS, Keycloak