Identity & Access Management
OAuth 2.0 & OpenID Connect
OAuth 2.0 is a delegated authorization protocol — it lets a client obtain limited access to a resource on a user's behalf — not an authentication protocol; OpenID Connect (OIDC) layers a verified identity (the ID token) on top of it to answer who the user actually is.
- OAuth 2.0 grants a client an access token scoped to act on a user's behalf against an API — it says nothing, by itself, about who the user is
- OpenID Connect (OIDC) is a thin identity layer on OAuth 2.0 that adds the ID token (a JWT describing the authenticated user) — this is what actually gives the client proof of identity
- Authorization Code + PKCE is the only grant recommended for modern clients (SPAs, mobile apps, server apps); the Implicit grant (tokens in the URL fragment) and Resource Owner Password Credentials grant (client handles raw credentials) are deprecated for leaking tokens and passwords
- Three distinct tokens: the authorization code (short-lived, single-use, exchanged at the token endpoint), the access token (presented to APIs), and the ID token (a JWT for the client to read) — the ID token is not a bearer credential and should never be sent to an API
- PKCE adds a
code_verifier/code_challengepair generated by the client: the authorization server only releases tokens to whoever holds the original verifier, closing the interception gap for public clients that cannot keep a secret - Scope narrows what a token can do; `aud` (audience) narrows who may accept it — tightening both limits how much damage a leaked token can cause
| Grant type | Use case | Status |
|---|---|---|
| Authorization Code + PKCE | SPAs, mobile apps, server-side web apps | Recommended for all modern clients |
| Client Credentials | Service-to-service, no user in the loop | Recommended for machine-to-machine |
| Refresh Token | Silently obtaining new access tokens | Recommended, paired with rotation |
| Implicit | Legacy SPAs (token returned directly in the redirect) | Deprecated — leaks tokens via browser history/referrer |
| Resource Owner Password Credentials | Legacy first-party trusted apps | Deprecated — exposes raw credentials to the client |
JwtParser parser = Jwts.parserBuilder()
.setSigningKey(issuerPublicKey) // RS256/ES256: verify with the issuer's public key
.build();
Jws<Claims> parsed;
try {
parsed = parser.parseClaimsJws(accessToken);
} catch (JwtException e) {
throw new UnauthorizedException("invalid signature or malformed token");
}
Claims claims = parsed.getBody();
if (!claims.getAudience().equals(expectedAudience)) {
throw new UnauthorizedException("token not intended for this API");
}
if (claims.getExpiration().before(new Date())) {
throw new UnauthorizedException("token expired");
}