Recommended resources
The freely available resources this lesson will direct you to.
Hello! Let's continue our journey into designing identity platforms.
In our last lesson, we designed a comprehensive system for managing scopes and user consent within an OAuth 2.0 framework. We established that OAuth is an authorization protocol, designed to grant applications limited access to resources. This naturally leads to a crucial question: how do we handle authentication—securely identifying who the user is in the first place?
This lesson addresses exactly that by focusing on our next learning outcome: Design the OIDC layer on top of OAuth 2.0, including ID token generation, claims management, and the UserInfo endpoint.
We will architect the components that transform our OAuth 2.0 authorization server into a full-fledged Identity Provider (IdP). Specifically, we will cover:
- The "Why" of OpenID Connect (OIDC): Understand the problem OIDC was created to solve.
- The ID Token: Design this core artifact, which is a JWT that proves a user's identity.
- Claims Management: Architect how clients request user information (claims) and how the server delivers it.
- The UserInfo Endpoint: Design a standard API for retrieving additional user attributes.
- Key Distribution with JWKS: Design the critical mechanism for securely validating ID tokens in a distributed system.
By the end of this lesson, you will understand how to build a modern, secure, and interoperable identity layer that enables federated login.
1. From Authorization to Authentication: The Need for OIDC
OAuth 2.0 became incredibly popular for delegated authorization. So popular, in fact, that developers started using it for something it was never designed for: authentication. They would treat the possession of a valid access token as proof of a user's identity. This led to a variety of custom, non-standard, and often insecure implementations.
The following video explains this history and why a formal extension was needed.
OAuth 2.0 and OpenID Connect (in plain English)
The video 'OAuth 2.0 and OpenID Connect (in plain English)' by OktaDev clearly explains how OAuth 2.0 was being misused for authentication and how OpenID Connect emerged to standardize this process.
Please watch these two segments: The Problem with OAuth for Authentication (45:23 - 48:57): This part covers the 'hacky' solutions that arose from using OAuth for login. Introducing OpenID Connect (48:57 - 50:35): This explains how OIDC adds a thin, standardized layer on top of OAuth 2.0 to formally handle authentication.
As the video highlighted, OpenID Connect (OIDC) isn't a replacement for OAuth 2.0. It's a thin identity layer built on top of it. It formalizes the process of authentication by adding a few key components to the standard OAuth 2.0 flow:
- A special scope:
openid - A new token: the ID Token
- A standard endpoint for user attributes: the UserInfo Endpoint
2. Architecting the OIDC Flow
To understand how OIDC works, it's critical to distinguish between the two types of tokens involved. The roles of the access token and the ID token are fundamentally different.
A Deep Dive into the Past, Present & Future of OAuth • Aaron Parecki • GOTO 2024
In 'A Deep Dive into the Past, Present & Future of OAuth', Aaron Parecki provides a perfect analogy for understanding the difference between access tokens and ID tokens.
Watch the segment from 13:32 to 16:18. Pay close attention to the distinction he makes: 'Access tokens are for the API, ID tokens are for the app'.
This distinction is the cornerstone of OIDC architecture:
- Access Token: An opaque string meant for a Resource Server (an API). It represents an authorization to access data. The client application should not try to read or interpret it.
- ID Token: A structured JWT meant for the Client Application (the "Relying Party"). It represents an authentication event and contains information about the user.
Let's see how these pieces fit together in the full OIDC flow.

The flow, from an architectural standpoint, is an extension of the OAuth 2.0 Authorization Code flow we're already familiar with:
- The client initiates the flow by redirecting the user to the authorization endpoint, but this time it must include
openidin thescopeparameter (e.g.,scope=openid profile email). - The user authenticates with the Identity Provider (our server) and consents to the requested scopes.
- The server redirects the user back to the client with an
authorization_code. - The client's backend exchanges the
authorization_codeat the/tokenendpoint. - This is the key OIDC step: Because
openidwas in the original scope, the/tokenendpoint returns not only anaccess_tokenbut also anid_token. - The client validates the
id_tokento authenticate the user and establish a session. - The client can later use the
access_tokento request more user information from the/userinfoendpoint.
3. Deep Dive: Designing the ID Token
The ID Token is a JWT containing claims about the authentication event and the user. As an architect, you need to ensure it's generated according to the OIDC specification.
The article 'OpenID Connect explained' by Connect2id provides an excellent breakdown of the ID Token's structure and its standard claims.
Please read section 3, 'The ID token'. Focus on the list of claims and the example JSON payload. This details the data contract your authorization server must fulfill when generating an ID token.
Based on the specification, your ID token generation service must populate these key claims:
iss(Issuer): The URL of your authorization server. This is a security measure so the client can verify the token came from the expected source.sub(Subject): A unique, stable identifier for the end-user at your server. This should never be recycled.aud(Audience): Theclient_idof the client application the token is intended for. The client must verify this claim to prevent a token issued for one client from being used at another.exp(Expiration Time): The time after which the ID token MUST NOT be accepted.iat(Issued At Time): The time at which the JWT was issued.auth_time: The time when the end-user authentication occurred.nonce: A string value used to mitigate replay attacks. The client generates anonce, includes it in the initial authentication request, and your server must include the samenoncein the ID token. The client then verifies it matches.
ID Token Validation and Key Distribution with JWKS
An ID token is useless if the client can't trust it. The client must validate the JWT's signature. This raises a critical architectural question: how does the client get the public key to verify the signature?
The solution is a JSON Web Key Set (JWKS). This is a standardized format for publishing your public keys at a well-known URL.

Here is how you would design this subsystem:
-
Generate a Key Pair and
kid: Your signing service uses a private key (e.g., RSA or EC) to sign ID tokens. You assign this key a unique Key ID (kid). Thiskidis a simple string, like"prod-signing-key-2023-10-26". -
Include
kidin the JWT Header: When signing an ID token, you must include thekidin the JWT's header.{ "alg": "RS256", "typ": "JWT", "kid": "prod-signing-key-2023-10-26" } -
Publish a JWKS Endpoint: You create a public, unauthenticated endpoint at a standard location:
/.well-known/jwks.json. This endpoint returns a JSON object containing an array of your public keys.{ "keys": [ { "kty": "RSA", "use": "sig", "kid": "prod-signing-key-2023-10-26", "alg": "RS256", "n": "0vx7agoebG...", "e": "AQAB" }, { "kty": "RSA", "use": "sig", "kid": "old-signing-key-2022-01-15", "alg": "RS256", "n": "...", "e": "AQAB" } ] }This design elegantly supports zero-downtime key rotation: you can add a new key to the JWKS, start signing with it, and clients will automatically pick it up while still being able to validate tokens signed with the old key.
-
Client-side Validation: When a client receives an ID token, it performs the following steps:
a. Parses the JWT to read thekidfrom the header (without verifying the signature yet).
b. Fetches your/.well-known/jwks.jsonendpoint (and caches the result for a short period, e.g., 15 minutes).
c. Finds the key in thekeysarray with the matchingkid.
d. Uses that public key to verify the JWT's signature.
JWKS and Zero-Downtime Key Rotation
The article 'JWKS and Zero-Downtime Key Rotation' provides a fantastic, engineering-focused guide to the design and implementation of a JWKS endpoint.
This is a great practical guide. Please read the following sections to understand the architectural design: What is JWKS?: The basic definition. The Problem JWKS Solves: The rationale for using this pattern over hard-coding keys. The Key ID (kid): Why It's Critical: Explains the role of the kid header. The Standard Location For JKWS: Explains the /.well-known/jwks.json convention. How JWKS Enables Zero-Downtime Key Rotation: Walks through the operational steps for a smooth key rotation.
4. Claims Management and the UserInfo Endpoint
Beyond the standard authentication claims, clients often need user profile information like name, email, or profile picture. OIDC standardizes this through additional scopes and the UserInfo endpoint.
Requesting Claims with Scopes
OIDC defines a set of standard scopes that map to sets of user claims. When a client includes these in its authorization request, it's signaling that it wants access to this information.
The 'OpenID Connect explained' article has a clear table mapping standard scopes to their associated claims.
Read section 7, 'Claims (user info)'. Pay attention to the table mapping scopes like profile and email to specific claims.
Your design should include a mapping service that translates requested scopes (profile, email, etc.) into a list of claims that the user has consented to share. The authorization server must then decide where to put these claims.
Architectural Choice: ID Token vs. UserInfo Endpoint
You have two standard ways to deliver these claims:
-
Inside the ID Token: You can embed claims like
name,email, etc., directly into the ID token's payload.- Pros: Fast. The client has all the information it needs immediately after token exchange, with no extra network calls.
- Cons: Can lead to large, bloated tokens. The information is only as fresh as the token itself.
-
Via the UserInfo Endpoint: You provide a separate API endpoint that returns the claims.
- Pros: Keeps the ID token small and focused on authentication. The data is always fresh, as it's fetched on demand.
- Cons: Requires an extra network call, adding latency.
A common and robust design is to include only the most essential claims in the ID token (like sub) and make all other profile information available via the UserInfo endpoint.
Designing the UserInfo Endpoint
The UserInfo endpoint is a simple, protected API that returns user claims.
OAuth 2.0 and OpenID Connect (in plain English)
The OktaDev video provides a concise explanation of the UserInfo endpoint's purpose.
Watch the short segment from 55:31 to 56:14. It describes how the UserInfo endpoint is used to get additional information not present in the ID token.
Here's how to architect it:
- Endpoint: A standard endpoint, e.g.,
/userinfo. - Request: A
GETorPOSTrequest to/userinfo. - Authorization: This is crucial. The request must be authenticated by including the
access_token(received from the/tokenendpoint) in theAuthorization: Bearerheader. The UserInfo endpoint is a resource server, and the access token authorizes access to it. - Response: The endpoint validates the access token. If valid, it looks up the user (
sub) and the scopes associated with the token. It then returns a JSON object containing only the claims the user consented to.
Example UserInfo response for an access token with openid and profile scopes:
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"picture": "http://example.com/janedoe/me.jpg"
}
Conclusion
In this lesson, we have designed the complete OIDC layer on top of our OAuth 2.0 server, effectively turning it into a modern Identity Provider. We moved beyond simple authorization to build a system capable of handling secure, federated authentication.
Key Takeaways:
- OIDC is an Authentication Layer on OAuth 2.0: It standardizes authentication by adding the
openidscope, the ID Token, and the UserInfo endpoint. - ID Tokens Prove Authentication: The ID token is a signed JWT for the client that contains claims about the user and the authentication event. It asserts who the user is.
- Access Tokens Authorize API Calls: The access token is for resource servers (like the UserInfo endpoint). It asserts what the client is allowed to do.
- JWKS is Essential for Secure Key Distribution: The
/.well-known/jwks.jsonendpoint is the standard, secure, and scalable mechanism for clients to get the public keys needed to validate ID tokens. - The UserInfo Endpoint Provides Additional Claims: It's a protected API, accessed with the access token, that delivers consented user attributes not included in the ID token.
Preview of the Next Lesson:
Now that we have a robust system for establishing a single, verified user identity, how do we leverage this across a landscape of multiple applications? In our next lesson, we will build upon our OIDC foundation to design advanced session management for federated identity, including single sign-on/sign-off (SSO) and channel-bound tokens. We will architect the systems that allow a user to log in once and gain access to many services seamlessly and securely.