Skip to main content
the auth layer

OAuth2 and OIDC Without the Marketing: What the Specs Actually Say

4 min read Chapter 7 of 45

OAuth2 and OIDC Without the Marketing

OAuth2 is a delegation framework. It lets a user grant limited access to their resources on one service to another service, without sharing their credentials. That is the entire purpose. It is not an authentication protocol. It does not tell you who the user is. It tells you what the user authorized a client to do.

OpenID Connect adds the authentication layer. It defines an ID token (a JWT) that tells the relying party who authenticated, when, and how. It defines a UserInfo endpoint for additional claims. It defines a discovery document so relying parties can auto-configure.

Most OAuth2 implementations in the wild conflate these two layers. They use access tokens as proof of identity. They treat the authorization code flow as a login flow without understanding why each parameter exists. They skip PKCE because “we are a confidential client.” Each of these shortcuts creates a specific, exploitable vulnerability.

This chapter traces the authorization code flow at the packet level, explains what attack each parameter prevents, and builds the base Spring Authorization Server configuration that subsequent chapters extend.

The OAuth2 Grant Types and When Each Is Correct

Authorization Code (with PKCE): A user is present. A client application needs access on behalf of that user. The user authenticates at the authorization server, grants consent, and the client receives an authorization code that it exchanges for tokens at the token endpoint. PKCE prevents authorization code interception even if the redirect is observed. This is the correct grant for the frontend shell, mobile client, and any user-facing integration.

Client Credentials: No user is present. A service needs access on its own behalf. The client authenticates directly at the token endpoint with its client ID and secret (or certificate). No authorization code, no redirect, no user interaction. This is the correct grant for the internal microservices when they access APIs that do not require user context.

Token Exchange (RFC 8693): A service has a token and needs a different token scoped to a different audience or with different claims. The service presents the original token at the token endpoint and receives a new token for the target service. This is the correct mechanism for user context propagation across service boundaries (covered in Chapter 9).

Refresh Token: Not a grant type in the traditional sense, but a mechanism for obtaining new access tokens without user interaction after the initial authorization. The client presents a refresh token at the token endpoint and receives a new access token (and optionally a new refresh token). This is covered in depth in Chapter 5.

Implicit and Resource Owner Password Credentials: Deprecated. The implicit flow is insecure (tokens in URL fragments, no PKCE). The password grant exposes user credentials to the client application. Neither should exist in new implementations. If you have either, migrate to authorization code with PKCE.

OpenID Connect on Top of OAuth2

OAuth2 gives you an access token. It does not define what that token contains or what it proves about the user. You cannot use an access token to answer “who logged in?” reliably, because the access token is designed for resource access, not identity assertion.

OpenID Connect adds:

  • ID Token: A JWT containing claims about the authentication event: who (sub), when (auth_time), how (acr/amr), and for whom (aud = the client). The ID token is consumed by the client, never sent to resource servers.
  • UserInfo Endpoint: An API that returns claims about the user when presented with a valid access token. Used for additional profile information beyond what the ID token contains.
  • Discovery: A .well-known/openid-configuration endpoint that publishes all endpoint URLs, supported scopes, signing algorithms, and capabilities. Clients auto-configure from this document.

The critical distinction: the access token is for resource servers. The ID token is for the client. Sending an ID token to a resource server as a bearer token is wrong. Using an access token to determine user identity is fragile (the access token may not contain user claims, and its audience is the resource server, not the client).