🧩 JWT vs OIDC — Separate but Together
1. JWT (JSON Web Token)
- What it is: A token format (just a container for claims).
- What it looks like:
header.payload.signature
- Who uses it: Both humans and services.
- What it carries: Facts like:
sub
→ who the token is aboutaud
→ who the token is forexp
→ when it expires
👉 JWT is like a passport card — it holds information and a signature that proves authenticity.
2. OIDC (OpenID Connect)
- What it is: A protocol (set of rules) built on top of OAuth2.
- Purpose: To let apps log users in and know who they are.
- What it gives you:
- ID Token → usually a JWT that says “This user is Alice, verified by Google/Okta/etc.”
- Access Token → used to call APIs (sometimes also a JWT).
👉 OIDC is like the border control process — the steps (passport check, stamps) that result in you getting that passport card (JWT) to travel with.
3. How they fit together
- JWT = the format
- OIDC = the login protocol that issues JWTs
So:
- A user logs in via OIDC.
- The IdP (e.g., Google, Okta, Cognito) hands back an ID Token, which is a JWT.
- Your app or API validates the JWT.
4. Use cases side by side
Situation | What you use | Example |
---|---|---|
Human login | OIDC → issues JWT (ID Token) | User signs in with Google → app gets ID Token JWT |
Service calling service | JWT directly (OAuth2 Client Credentials) | Microservice A calls Microservice B using JWT access token |
API Gateway | Validates JWTs (from OIDC or service flows) | AWS API Gateway checks JWT from Cognito or Firebase |
5. 🔑 Key takeaway
- JWT is a token format.
- OIDC is a login protocol.
- OIDC tokens are JWTs.
- For users → OIDC (login flow) → JWTs.
- For services → JWTs (via OAuth2 or workload identity).
So yes — separate (format vs protocol), but always together in practice.
JWT & OIDC — The Plain-English Guide (from Fundamentals to Advanced)
0) The 1-minute mental model
- JWT = a sealed card (digitally signed) that carries facts (“who/what/when/permissions”). Anyone with the issuer’s public key can check it hasn’t been altered.
- OIDC (OpenID Connect) = a login recipe for humans built on OAuth2. It tells apps how to log people in (redirects, consent) and returns an ID Token (a JWT) that says who the user is.
- Access Token (often a JWT) = a permit you show to an API to prove what you’re allowed to do.
- Refresh Token = a backstage pass the app uses to quietly get new access tokens without asking the user to log in again.
1) What is JWT?
A compact, URL-safe token format: header.payload.signature
.
- Header: the signing algorithm (e.g., RS256).
- Payload: claims like
sub
(subject/user),aud
(audience),exp
(expiry). - Signature: proves the token was issued by the trusted issuer and not modified.
Key property: self-contained. You can validate it offline using the issuer’s public key (published via JWKS URL).
2) What is OIDC?
A standard way to log users in (web/mobile) using OAuth2. It defines:
- ID Token (JWT) → proves who the user is (identity).
- UserInfo endpoint → optional place to fetch profile data.
- Discovery document → where the app finds all endpoints & keys.
OIDC ≈ login flows; JWT ≈ the token format OIDC uses for identity.
3) Why do we need them?
- Single Sign-On across apps, teams and even companies.
- Zero shared secrets between every service—verify tokens with public keys.
- Stateless performance—no per-request DB lookup needed to validate tokens.
- Portable and interoperable—works across clouds and languages.
4) Common use-cases
Human users (OIDC)
- “Login with Google/Okta/Azure AD/Keycloak/Cognito.”
- Multi-app SSO (one login, many apps).
- MFA, passwordless, social login.
Service ↔ Service (JWT)
- One microservice calling another with a short-lived JWT (often from OAuth2 Client Credentials).
- Jobs, webhooks, backends calling APIs without a human user.
Mixed
- User logs in (OIDC) → frontend calls your API with an access token (JWT).
- Backend calls downstream services using its own service JWT.
5) How they work (step by step)
A) OIDC “Authorization Code + PKCE” (recommended for web/SPAs/mobile)
- User opens your app → “Sign in”.
- App redirects the user to the Identity Provider (IdP) (e.g., Google/Okta/Keycloak/Cognito/Firebase).
- User authenticates (password, MFA, passkey).
- IdP redirects back with a code.
- Your app (or backend) exchanges the code at the IdP’s Token Endpoint → gets:
- ID Token (JWT) = who the user is.
- Access Token (often JWT) = what they can do.
- Refresh Token (optional) for silent renewals.
- App calls your APIs with the access token in
Authorization: Bearer <token>
. - APIs validate the token (issuer, audience, signature, expiry) and proceed.
PKCE: protects SPAs/mobile apps by proving the app that started the login is the app finishing it.
B) Service-to-Service (OAuth2 Client Credentials)
- Service A asks the IdP/Authorization Server for a token using its client id/secret (or mTLS).
- IdP issues a short-lived access token (JWT) with claims like
aud
=ServiceB, scopes/roles. - Service A calls Service B with
Authorization: Bearer …
. - Service B validates: signature, issuer, audience, exp (and optionally scope/role claims).
C) Token validation (what every API should check)
- Signature: verify with the issuer’s JWKS public key.
- Issuer (
iss
): matches the IdP you trust. - Audience (
aud
): matches your API. - Expiry (
exp
) and not before (nbf
). - Subject (
sub
) present; optionally scope/roles to authorize actions.
6) Advanced (without the jargon)
- Scopes vs Roles vs Claims
- Scopes = what actions are allowed (e.g.,
read:orders
). - Roles = grouped permissions (e.g.,
admin
,editor
). - Claims = facts inside the token (email, org, roles, tenant).
- Scopes = what actions are allowed (e.g.,
- Key types
- HS256 (shared secret) → simple but you must protect the secret on every verifier.
- RS256/ES256 (public/private keys) → issuer keeps private key, everyone else only needs public key (safer & common).
- Key rotation
- IdP exposes JWKS. Your gateways/servers should cache keys and auto-refresh (handle
kid
changes seamlessly).
- IdP exposes JWKS. Your gateways/servers should cache keys and auto-refresh (handle
- Refresh tokens
- Keep them server-side or in HTTP-only secure cookies (not localStorage).
- Rotate on use; revoke on breach.
- Opaque vs JWT access tokens
- Opaque: must call the issuer’s introspection endpoint each time.
- JWT: validate offline; faster, but revocation is trickier.
- Proof-of-Possession (DPoP/mTLS)
- Binds the token to a specific client key or TLS certificate → blocks token theft/replay.
- Token exchange / impersonation
- Trade one token for another (e.g., user token → service token limited to one API).
- Logout
- Front-channel (browser) or back-channel (server-to-server) logout, plus session cookies invalidation and refresh token revocation.
7) Limitations & gotchas (and how to avoid them)
- Revocation is hard with JWT
Use short lifetimes (e.g., 5–15 min) + refresh tokens. Revoke refresh tokens on logout/compromise. - Too many claims = big tokens
Keep tokens lean; fetch extra data from your user/profile API when needed. - Audience/issuer mismatches
Many 401s boil down to wrongaud
oriss
. Double-check both. - Clock skew
Allow 1–2 minutes skew oniat/nbf/exp
. - Storing tokens in the browser
Prefer HTTP-only secure cookies over localStorage (defense against XSS). - “alg: none” / weak algorithms
Only accept expected algorithms (e.g., RS256). Never allownone
.
8) Where they fit in AWS, Google Cloud, Azure
AWS (Amazon)
- Issuers / IdPs
- Amazon Cognito User Pools (OIDC & OAuth2; issues ID/Access/Refresh tokens as JWTs).
- IAM OIDC/Federation (workload identity, GitHub Actions, IRSA for EKS).
- External IdPs: Okta, Auth0, Keycloak, Google (federated to Cognito or directly to ALB/API GW).
- Validators / Enforcement
- API Gateway (HTTP API) → JWT Authorizer (validates issuer/jwks/audience).
- Application Load Balancer → authenticate-oidc action (runs the OIDC login flow) with Cognito/Okta/Keycloak/Google (note: not Firebase as an OIDC provider for ALB).
- EKS: NGINX/Kong/Envoy/Istio gateways can validate JWTs; IRSA uses OIDC to map pods to IAM roles.
- CloudFront: integrates with Cognito/custom auth at the edge (via Lambda@Edge).
Google Cloud
- Issuers / IdPs
- Identity Platform / Firebase Auth (OIDC/OAuth2; issues ID tokens as JWTs).
- Google Workspace/Cloud Identity for enterprise SSO.
- Validators / Enforcement
- API Gateway / Cloud Endpoints → JWT/OIDC validation in the gateway config.
- Cloud Run: private services trust Google-signed ID tokens (IAM); put Gateway in front to validate external JWT/OIDC.
- IAP (Identity-Aware Proxy) for web access control.
- Load balancers + Cloud Armor (policy/WAF) in front of Gateways/Backends.
Microsoft Azure
- Issuers / IdPs
- Microsoft Entra ID (formerly Azure AD) and Entra ID B2C (OIDC/OAuth2).
- Validators / Enforcement
- API Management (APIM) → validate JWT (issuer/jwks/audience), add policies.
- App Service Authentication (“EasyAuth”) → offload OIDC login to Entra/others.
- AKS: NGINX/Kong/Envoy/Istio for JWT validation; Workload Identity uses OIDC to map pods to Azure managed identities.
9) Quick recipes you can reuse
✅ Human login (web/app) — safest baseline
- OIDC Authorization Code + PKCE
- IDP: Cognito / Entra ID / Identity Platform (Firebase Auth) / Okta / Auth0 / Keycloak
- Store access/refresh tokens in HTTP-only secure cookies
- Rotate refresh tokens; short access token TTL (5–15 min)
✅ Service → Service
- OAuth2 Client Credentials (mTLS if possible)
- Short TTL (5–10 min), audience-scoped to the target service
- Validate on the API: signature,
iss
,aud
,exp
, scopes/roles
✅ Ingress/Gateway enforcement
- AWS: API Gateway JWT Authorizer or ALB OIDC (with Cognito/Okta/etc.)
- GCP: API Gateway (JWT/OIDC) → Cloud Run (private)
- Azure: APIM JWT policy or EasyAuth
10) Security checklist (copy/paste)
- Access tokens expire ≤ 15 minutes; refresh tokens rotate.
- Validate signature, iss, aud, exp/nbf on every API call.
- Accept only the algorithms you expect (e.g., RS256).
- Enforce least privilege with scopes/roles.
- Use PKCE for SPAs/mobile.
- Use HTTP-only secure cookies for tokens on the web.
- Implement logout (revoke refresh tokens, clear cookies, end IdP session).
- Monitor JWKS fetch/rotation; handle
kid
changes. - Add WAF/rate limits at your gateway/load balancer.
- Log token ID (
jti
),sub
,aud
(no sensitive data) for audits.
11) FAQ (super short)
Is JWT only for machines and OIDC only for humans?
- JWT is a format used by both. OIDC is a login protocol for humans that uses JWT (ID Token).
Do I need OIDC for service-to-service?
- Usually no. Use OAuth2 Client Credentials (returns a JWT access token).
Can I revoke a JWT immediately?
- Not cleanly. Use short lifetimes + refresh token revocation. For high-risk actions, re-check with the IdP or use introspection.
What’s the difference between an ID Token and an Access Token?
- ID Token: identity of the user (for your client).
- Access Token: permission for an API (what can be done).
12) Your migration hint (EKS front door options)
- Mirror GCP approach: AWS API Gateway (HTTP API) JWT Authorizer validating Firebase/Auth0/Okta/Keycloak → VPC Link → EKS.
- All inside EKS: ALB Ingress → in-cluster gateway (NGINX/Kong/Envoy) validates JWT.
- ALB runs login: use Cognito/Okta/Keycloak/Google for ALB OIDC (Firebase doesn’t act as ALB’s OIDC IdP).
let’s expand that simple table into detailed workflows so you can see exactly what happens at each hop, for Human login (OIDC), Service-to-Service (JWT), and API Gateway validating JWTs.
🔄 Detailed Workflows
1. 👤 Human Login (OIDC → issues JWT ID Token)
Actors: User, Application (frontend + backend), Identity Provider (Google, Okta, Cognito, Firebase, etc.), API
Step-by-step
- User opens app
- User clicks “Sign in”.
- App redirects them to the IdP (OIDC provider).
- IdP Login Page
- User enters username/password, MFA, or social login.
- IdP authenticates them.
- Authorization Code issued
- After login, IdP redirects back to app with a code (short-lived, single use).
- This avoids exposing tokens in browser URL.
- App exchanges code for tokens
- App/backend calls IdP Token Endpoint with:
- The code
- Client ID/secret
- PKCE verifier (for extra security in mobile/SPAs)
- IdP responds with:
- ID Token (JWT) → “User is Alice”
- Access Token (JWT/opaque) → “Alice can call API X”
- Refresh Token (optional).
- App/backend calls IdP Token Endpoint with:
- App calls API with Access Token
Authorization: Bearer <access_token>
.
- API validates Access Token
- Check signature (using IdP’s JWKS keys).
- Check claims (
iss
,aud
,exp
). - Enforce scopes/roles.
- API returns response
- If valid, API serves data → app → user.
✅ Key JWT role here: OIDC issues an ID Token (JWT) for identity, and an Access Token (often JWT) for authorization.
2. 🔗 Service-to-Service (JWT directly, OAuth2 Client Credentials)
Actors: Service A (caller), Service B (target API), Identity Provider / Authorization Server
Step-by-step
- Service A needs to call Service B
- Example: Payment service → Order service.
- Service A requests token
- Calls IdP’s Token Endpoint with:
client_id
,client_secret
(or mTLS certificate).- Grant type:
client_credentials
.
- Calls IdP’s Token Endpoint with:
- IdP issues Access Token (JWT)
- Payload includes:
iss
(issuer = IdP)aud
(audience = Service B)sub
(subject = Service A)exp
(expiry, e.g., 5 mins)scope
(permissions likeorders:read
).
- Payload includes:
- Service A calls Service B
- HTTP request with
Authorization: Bearer <access_token>
.
- HTTP request with
- Service B validates token
- Retrieves IdP’s JWKS keys.
- Verifies signature, expiry, audience = Service B.
- Enforces scopes (only allowed actions).
- Service B responds
- Request succeeds if valid.
- Otherwise returns
401 Unauthorized
.
✅ Key JWT role here: The JWT itself is the service identity. No human involved.
3. 🌐 API Gateway validates JWT (edge enforcement)
Actors: Client (user or service), API Gateway (AWS API Gateway / GCP API Gateway / Azure APIM / NGINX/Kong), Backend service
Step-by-step
- Client sends request
- Attaches
Authorization: Bearer <token>
(could be user OIDC token or service JWT).
- Attaches
- Gateway receives request
- Gateway is configured with issuer and JWKS URI.
- Gateway validates JWT
- Check signature against IdP’s public keys.
- Check issuer matches trusted IdP.
- Check audience claim matches the API.
- Check expiry.
- Optionally check scopes/roles.
- If valid → forward request to backend.
- Adds headers (like
X-User-Id
,X-Scopes
) if configured. - Backend trusts Gateway because it knows only validated requests get through.
- Adds headers (like
- If invalid → reject at Gateway.
- Return
401 Unauthorized
(or403 Forbidden
). - Backend never sees bad traffic.
- Return
✅ Key JWT role here: Gateway uses JWT to decide at the edge whether traffic should even hit the backend.
📊 Connected View (How they all tie together)
(1) Human Login (OIDC)
User -> OIDC Provider -> ID Token (JWT) + Access Token -> API Gateway -> API
(2) Service-to-Service (JWT)
Service A -> IdP (OAuth2 Client Credentials) -> Access Token (JWT) -> Service B
(3) API Gateway (Validator)
Any Client -> API Gateway -> JWT Validation -> Backend Service
🔑 Takeaway
- JWT = the envelope (claims + signature).
- OIDC = the login flow (for humans) that issues JWTs.
- Service-to-service uses JWTs directly (no OIDC flow needed).
- Gateways act as bouncers, validating JWTs before APIs are touched.