What OAuth 2.0 governs and what it doesn't
OAuth 2.0 is defined in RFC 6749 and describes an authorization framework, not an authentication method. It solves the problem that an application (the Client) needs to access a protected API (the Resource Server) on behalf of a user (the Resource Owner), without obtaining that user's credentials. Instead, an Authorization Server issues the client an Access Token that grants limited access.
The framework knows several ways to obtain a token (grant types):
- Authorization Code - for applications with user interaction, the recommended standard path.
- Client Credentials - for machine-to-machine access without a user.
- Implicit and Resource Owner Password Credentials - historical methods, now discouraged (see below).
Important: OAuth itself says nothing about who the user is. An access token only proves that the client has been granted a certain access, not the identity of the logged-in user. The OWASP project states this explicitly in API2:2023: OAuth is not authentication, and API keys aren't either. Anyone who misuses an access token as proof of identity builds Broken Authentication into their system.
| Characteristic | ID Token | Access Token |
|---|---|---|
| Purpose | Authentication: proves who logged in | Authorization: grants access to an API |
| Recipient (audience) | The client (OIDC relying party) | The resource server (the API) |
| Format | Always a JWT with defined claims (iss, sub, aud, exp) | Opaque to the client (often a JWT, but can also be opaque) |
| Defined in | OpenID Connect Core 1.0 | OAuth 2.0 (RFC 6749), usage as bearer in RFC 6750 |
| Typical mistake | Mistakenly presented to the API as an access token | Mistakenly interpreted as proof of identity |
ID Token versus Access Token in OpenID Connect
Authorization Code Flow with PKCE
The Authorization Code Flow separates two channels: via the browser (front channel), the client only receives a short-lived authorization code; the actual token is then redeemed via a direct server-to-server request (back channel) at the token endpoint. This way, the access token never ends up in the URL or the browser history.
The flow in steps:
- The client redirects the user to the
authorizeendpoint withresponse_type=code. - After login and consent, the user is redirected with a
codeback to the registeredredirect_uri. - The client exchanges the
codeat thetokenendpoint for an access token (and optionally a refresh token).
PKCE (Proof Key for Code Exchange, RFC 7636) hardens this flow against interception and injection of the code. Before starting, the client generates a random secret (code_verifier), sends only its hash (code_challenge) with the authorization request, and must prove the matching code_verifier when exchanging the token. An intercepted code alone is thus worthless. Originally developed for native apps, PKCE today, according to RFC 9700 and the OAuth 2.1 draft, applies to all client types, including web applications and confidential clients, since they too are vulnerable to code injection attacks.
Token types, scopes, and refresh tokens
Access Token: the actual access token for the API. Mostly used as a bearer token (RFC 6750): whoever holds it may use it, no cryptographic proof of possession is required. It is transmitted in the Authorization: Bearer header. Access tokens should be short-lived.
Refresh Token: a longer-lived token that lets the client fetch a new access token from the authorization server when the old one expires, without renewed user interaction. According to RFC 6749, refresh tokens are intended exclusively for the authorization server and are never sent to the resource server. They can also be used to request tokens with the same or a narrower scope.
Scopes: via the scope parameter, the client requests a set of permissions (e.g. read, write). The authorization server may ignore the requested scope in whole or in part and must return the actually granted scope in the response if it deviates. Scopes are coarse-grained permissions; they don't replace object-level authorization: which specific record is accessible must additionally be checked by the API itself, otherwise BOLA vulnerabilities threaten.
OpenID Connect: the identity layer
OpenID Connect (OIDC) is a layer on top of OAuth 2.0 that supplies the missing piece: a standardized proof of who logged in. At its core is the ID Token, a JWT with claims about the end user's authentication. Mandatory fields include, among others, iss (issuing server), sub (unique user ID), aud (which client the token is intended for), and timestamps.
The crucial difference:
- The ID Token is addressed to the client and answers the question of authentication. It is not meant to be presented to the API as an access token.
- The Access Token is addressed to the API (the resource server) and answers the question of authorization. Its format is opaque to the client.
If a client wants additional profile data, it calls the UserInfo endpoint with the access token, which returns claims about the end user. A common mistake is mixing up the ID token and the access token, for example sending the ID token to the API, or conversely deriving identity data from an access token that it doesn't actually guarantee.
Common mistakes and the link to Broken Authentication
Most OAuth/OIDC vulnerabilities do not arise in the protocol itself, but in faulty implementation, and fall under OWASP's API2:2023 Broken Authentication.
- Implicit flow: with the implicit grant (
response_type=token), the authorization server delivers the access token directly in the redirect URL. Tokens in the URL end up in logs, browser history, and theRefererheader, and cannot be bound to a client.RFC 9700advises against it, and OAuth 2.1 removes the implicit flow entirely. The Authorization Code Flow with PKCE is recommended instead. - Token in the URL: even independent of the implicit flow, passing access tokens as query parameters is problematic.
RFC 6750rates the URI query parameter method asSHOULD NOT, because the URL is very likely to be logged. Tokens belong in theAuthorizationheader. - Missing audience check: the API must verify that the presented token is actually intended for it.
RFC 9700recommends restricting access tokens to a specific resource server via theaudclaim. Without this check, a token issued for service A can be accepted by service B (token reuse). - Missing scope and signature checks: if scope isn't enforced server-side, or the token's signature and expiry aren't validated, privilege escalation and forged tokens become possible. More on this under JWT Attacks.
- Loose
redirect_urivalidation: OAuth 2.1 requires an exact string comparison of the redirect URI. Wildcards or substring matching allow codes or tokens to be redirected to attackers.
OAuth 2.1: consolidating best practices
OAuth 2.1 is not a new protocol, but a consolidation: the internet draft draft-ietf-oauth-v2-1 combines RFC 6749, RFC 6750, and the security recommendations established since then (particularly RFC 9700) into a single document and removes outdated, insecure parts.
The most important changes compared to OAuth 2.0:
- PKCE is mandatory for the Authorization Code Grant, for all client types.
- The Implicit Grant (
response_type=token) is removed. - The Resource Owner Password Credentials Grant (passing passwords to the client) is removed.
- Redirect URIs must be validated via exact string comparison.
- Bearer tokens may no longer be transmitted in the URL query string.
As a rule of thumb for a secure implementation: Authorization Code Flow with PKCE, short-lived access tokens, strict validation of signature, aud, exp, and scope on the API side, and a clear separation of ID token (authentication) and access token (authorization).
Sources
- RFC 6749 - The OAuth 2.0 Authorization Framework IETF, 2012
- RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage IETF, 2012
- RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients IETF, 2015
- RFC 9700 - Best Current Practice for OAuth 2.0 Security IETF, 2025
- The OAuth 2.1 Authorization Framework (draft-ietf-oauth-v2-1) IETF OAuth Working Group, 2025
- OpenID Connect Core 1.0 OpenID Foundation, 2023
- API2:2023 Broken Authentication - OWASP API Security Top 10 OWASP, 2023