Getting to know OpenID Connect as an identity layer on top of OAuth 2.0: the structure of the ID token as a JWT, standard claims, the main OIDC flows, the UserInfo endpoint, and configuration discovery via well-known and JWKS.

For the last four episodes you worked with pure OAuth 2.0: tokens, scopes, and consent. Episode 9 opens a new phase — OpenID Connect (OIDC). If OAuth 2.0 answers "who is allowed to access what", OIDC answers a more fundamental question: "who is this user, and how does the application know?" OIDC is the standard answer Keycloak uses as an identity provider.
OIDC is an identity layer built on top of OAuth 2.0. It doesn't replace OAuth — it adds what was previously missing:
The key behind the scenes: the OAuth 2.0 access token still exists, but now there's a second token called the ID token that serves as proof of authentication.
OIDC inherits the OAuth 2.0 flows, each with its own nuance:
A safe rule of thumb: use the authorization code flow with PKCE. The other two flows are only for compatibility.
An ID token is a JWT (JSON Web Token) — three parts, each base64url-encoded and separated by dots:
alg, kid).Example ID token payload:
{
"exp": 1754213000,
"iat": 1754212700,
"auth_time": 1754212700,
"jti": "a3f2c1d0-...",
"iss": "https://kc.example.com/realms/my-realm",
"aud": "my-app",
"sub": "9c4d5e6f-...",
"typ": "ID",
"azp": "my-app",
"nonce": "1a2b3c4d",
"name": "Budi Santoso",
"preferred_username": "budi",
"email": "budi@example.com",
"email_verified": true
}See that typ has the value ID — this is the marker that this token is indeed an ID token, not an access token.
| Aspect | ID Token | Access Token |
|---|---|---|
| Purpose | Proving user authentication | Granting access to an API |
| Content | Identity claims | Authorization claims and roles |
| Usage | Consumed by the client app | Sent to the resource server |
| Format | Always a JWT | JWT or opaque |
| Distinctive claims | sub, nonce, auth_time | scope, realm_access, resource_access |
The classic mistake: sending the ID token as a Bearer token to an API. The ID token isn't a replacement for the access token — a resource server shouldn't trust a token whose audience is the client application.
OIDC defines standard claims that are required or commonly present in the ID token:
On top of the standard claims, Keycloak can add custom claims via protocol mappers — e.g. user attributes or roles. This is covered in depth in episode 10.
Accepting an ID token without validation is like letting anyone in. The required steps:
iss exactly matches the realm's issuer URL.aud contains your client ID.exp hasn't passed and iat isn't unreasonably far in the future.nonce stored when starting the request.curl -s "https://kc.example.com/realms/my-realm/protocol/openid-connect/certs"protocol/openid-connect/certs returns the public keys in JWK format. In most frameworks, this validation is handled by middleware — but you must understand what's being checked.
Besides the claims inside the ID token, OIDC provides a UserInfo endpoint for fetching the user profile separately:
curl -s -H "Authorization: Bearer eyJhbGciOi..." \
"https://kc.example.com/realms/my-realm/protocol/openid-connect/userinfo"The response is JSON containing the claims permitted by the approved scopes. Because it's fetched with an access token, the UserInfo response can be richer than the ID token — that's a claims strategy question covered in episode 10.
OIDC wraps all endpoint addresses in a single public metadata file:
curl -s "https://kc.example.com/realms/my-realm/.well-known/openid-configuration"{
"issuer": "https://kc.example.com/realms/my-realm",
"authorization_endpoint": "https://kc.example.com/realms/my-realm/protocol/openid-connect/auth",
"token_endpoint": "https://kc.example.com/realms/my-realm/protocol/openid-connect/token",
"jwks_uri": "https://kc.example.com/realms/my-realm/protocol/openid-connect/certs",
"userinfo_endpoint": "https://kc.example.com/realms/my-realm/protocol/openid-connect/userinfo",
"end_session_endpoint": "https://kc.example.com/realms/my-realm/protocol/openid-connect/logout",
"grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"]
}With this metadata, any OIDC library can configure itself — just give it the issuer URL and the rest is discovered automatically. /.well-known/openid-configuration is the gateway to every integration.
OIDC also defines dynamic client registration — a client registers itself with Keycloak via a request without admin intervention. Keycloak supports it at the registration endpoint, but the feature must be explicitly enabled in Realm settings because it opens the door to automatic registration. For strict production environments, leave it off and register clients manually.
In episode 9 you got to know OIDC: its position as an identity layer on top of OAuth 2.0, its main flows, the JWT structure of the ID token, standard and custom claims, token validation steps, the UserInfo endpoint, discovery metadata, and dynamic client registration.
Key takeaways:
iss, aud, exp, the signature, and nonce.In the next episode (episode 10), you'll dissect OIDC claims and the user profile — what standard claims are available, how to add custom attributes via protocol mappers, and when to use the ID token or the UserInfo endpoint.