OIDC + JWT
Historic Local Auth
To get users to authenticate to your services, usually you'd just store a DB with usernames and passwords
Most of the time this just becomes downright annoying, and at the end of the day most people don't want to create Yet Another Username Password (YAUP)
We can move away from this model towards one where user authentication is done via dedicated, federated services...aka logging into one app with user/pass auth of another app
This other apps / provider is called an Identity Provider (IdP)
Typical IdP providers include Google, Facebook, Twitter, and Apple
OIDC
OIDC, or OpenID Connect, is a way to ensure standard protocols and architecture around IdP provisioning
Most ID Tokens are JWT's
- Identity Provider (IdP) Issuer
- Trusted service that handles user auth itself
- Issues tokens like JSON Web Tokens (JWT) a.k.a ID Tokens, Access Tokens, etc...
- User
- User or app that's trying to authenticate
- They login / authenticate to the IdP
- Desired Server
- Backend server hosting the protected resources (API, Database, Data Warehouse)
- Verifies the token issued by the IdP to grant or deny access
- Typically done via
JWKS
lookups - IdP might send a random token...doesn't mean that user is even for you
- Often by validating the ID Token or Access Token signature and claims
- Typically done via
- JWT
- A single string with 3 base64url-encoded parts which are separated by dots
- Compact, URL-safe, self-contained tokens
header.payload.signature
- Header
- Which signing algorithm was used
- Token type (usually just
JWT
) - KeyID
kid
to indicate which public key to use
- Payload AKA Claims -The actual identity and metadata values
- Signature
- A cryptographic signature of the header+payload made with the issuer's private key
- Header
OpenID Flow
Below we discuss different parts of OpenID Flow
Public/Private Key Usage
There still need to be public and private keys used under typical Alice-Bob trust scenarios
- IdP Issuer
- They will have a private key
- It signs an incoming
header+payload
JWT using this private key
- Client / Resource Server
- The verifier fetches the IdP's public key from a well known JWKS Endpoint - typically
./well-known/jwks.json
- It uses this specific public key to verify:
- The token's signature matches the
header+payload
- This ensures we know the token really came from the IdP and hasn't been tampered with
- The token's signature matches the
- The verifier fetches the IdP's public key from a well known JWKS Endpoint - typically
Verification Process
- How do backend services offload auth?
- When a new request comes in, you will re-route requests to another service, an IdP, who will tell you yes/no they are who they are
- Some even do authorization on yes/no they have right to do this action
- How does a backend service verify a signed JWT?
- Split JWT into header, payload, signature.
- Decode header and get kid to know which public key to fetch from IdP.
- Fetch JWKS and select the correct public key.
- Recalculate the signature over header.payload using the public key and see if it matches the JWT’s signature.
- If signature matches safe to trust that payload was issued by IdP.
Claims
Claims are just k:v
pairs in the payload that provide information on the authentication, authorization, and scope of token
{
"iss": "https://accounts.google.com", // Issuer
"sub": "110169484474386276334", // Subject (user ID at issuer)
"aud": "your-client-id.apps.googleusercontent.com", // Audience (who this token is for)
"exp": 1716239022, // Expiration time (Unix timestamp)
"iat": 1716235422, // Issued at
"name": "Jane Doe", // Profile info
"email": "janedoe@example.com",
"email_verified": true,
"roles": ["admin"] // Custom claim
}
These are not encrypted at all, and anyone could hypothetically read them, but they are protected by the signature. If any of them change, the signature we calculate with public key would no longer match the one from private key
AWS Example
In the below example we'd have an API GW that acts as a proxy and helps us to automate some authorization tasks
Vocabulary:
- JWKS Lookup: This is how the API GW verifies the JWT without needing an OIDC private key - it can use the public keys from
jwks_uri
- Claims: