OAuth ≠ authentication
An access token is an authorization credential, not a login assertion for the relying app. If your application needs to know who the user is, use OpenID Connect on top of OAuth and validate the security-relevant ID token claims required for the flow.
Flow and layering
- No code path uses a raw access token as a login assertion for the client application. If the app needs to know who the user is, it uses OIDC and reads identity from a validated ID token.
- The login flow obtains an ID token. The UserInfo endpoint may be called with an access token for additional claims, but session identity is anchored to validated ID token claims, not to UserInfo output alone. If UserInfo is used, its sub claim must exactly match the validated ID token's sub.
- The user's stable internal ID is the tuple (iss, sub), never sub alone (only unique within an issuer), and never email or preferred_username (mutable and attacker-influenceable).
ID token claim validation
- The iss claim is compared exact-match against the expected issuer URL. Not startsWith, not a regex.
- The aud claim contains this application's registered client ID. If aud has multiple values, the token is rejected unless the additional audiences are explicitly trusted. If azp is present, it equals this client ID unless the integration has a documented reason otherwise.
- The exp claim is enforced with a tight clock-skew window (at most 120 seconds); iat is used for freshness, and tokens older than a reasonable bound are rejected even if not expired.
- If a nonce was sent on the authorization request, the nonce claim on the ID token is verified against the session-bound value and consumed, never accepted twice.
Signature verification
- Every ID token signature is verified against the IdP's JWKS. The expected algorithm is pinned (typically RS256 or ES256) and never accepted from the token header. The none algorithm is rejected.
- JWKS is refreshed on signature-verification failure to handle key rotation, rate-limited to prevent denial of service against the IdP. A missing or unknown kid fails closed and never falls back to unverified.
