OAuth 2.0 and OpenID Connect
OAuth delegates access
OAuth 2.0 lets a client obtain limited access to a protected resource. Four roles appear in the protocol: the resource owner, the client, the authorization server, and the resource server that hosts the protected data. One deployment can combine server roles, but their responsibilities stay distinct.
An access token is the credential the client presents to the resource server. It represents authorization with particular scope and duration. OAuth does not require an access token to be a JWT. The token can be opaque to the client.
Wrong "A valid access token proves the user logged in."
Right OAuth defines delegated authorization. OpenID Connect adds an authentication result for the client.
The authorization-code flow with PKCE
For an interactive flow, the client sends the user's browser to the authorization endpoint. The authorization server authenticates the user and obtains an authorization decision. It redirects the browser back with a short-lived, single-use authorization code. The client exchanges that code at the token endpoint.
PKCE binds this exchange to the client instance that started it. The client creates a fresh random code_verifier for each request. It sends an S256-derived code_challenge in the authorization request:
GET /authorize?response_type=code
&client_id=web-client
&redirect_uri=https%3A%2F%2Fclient.example%2Fcallback
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
The callback contains the code, not the access token. The client then sends the original verifier to the token endpoint:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=RETURNED_CODE&code_verifier=ORIGINAL_VERIFIER
The authorization server transforms the verifier and compares it with the challenge stored for the code. An attacker who intercepts only the code cannot complete that check. The current OAuth security BCP says this guidance applies to every kind of OAuth client, including web applications, and identifies S256 as the challenge method that does not expose the verifier in the authorization request.
Wrong "PKCE is a client secret for browser apps."
Right A PKCE verifier is transaction-specific. A public client cannot keep a shared client credential confidential.
Access, refresh, and ID tokens
A refresh token goes to the authorization server's token endpoint to obtain another access token. Issuing one is optional. It is not sent to the resource API. A refresh request cannot expand beyond the scope originally granted.
OpenID Connect requests include the openid scope. OIDC adds the ID token, a JWT containing claims about the user's authentication by the authorization server for the client. In the authorization-code flow, the token response contains an ID token and an access token.
The recipients differ:
# Call the protected API
Authorization: Bearer ACCESS_TOKEN
# Refresh through the authorization server
grant_type=refresh_token&refresh_token=REFRESH_TOKEN
The client validates and consumes the ID token. It does not substitute that ID token for the access token expected by an API.
Choose a grant by who is acting
OAuth calls the credential used to obtain an access token an authorization grant. RFC 6749 defines authorization code, implicit, resource owner password credentials, and client credentials, plus an extension mechanism for other grants.
Authorization code fits an interactive user flow. The authorization server, rather than the client, receives the user's credentials. Current security guidance says public clients must use PKCE. It also applies PKCE guidance to confidential web applications.
Client credentials has no interactive user. A confidential client authenticates at the token endpoint and acts on its own behalf, or uses access arranged in advance. RFC 6749 says this grant must only be used by confidential clients and says its response should not include a refresh token.
POST /token
Authorization: Basic ENCODED_CLIENT_CREDENTIALS
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=jobs%3Arun
Wrong "Client credentials is authorization code without the browser."
Right The subject and authorization context differ. Client credentials normally represents the client, while authorization code carries a user's authorization through an interactive redirect flow.
Why the older grants are poor defaults
The implicit grant returns an access token from the authorization endpoint. RFC 9700 says clients should not use it unless they prevent token injection and mitigate its token leakage vectors. A code response keeps the access token out of the authorization response and lets the authorization server detect replay during the code exchange.
The resource owner password credentials grant gives the user's password to the client. RFC 9700 says it must not be used. It increases the places where credentials can leak and does not support authentication processes that need multiple user interaction steps.
Extension grants still exist. RFC 6749 lets a client send an absolute URI as grant_type plus parameters defined by that extension. Do not describe every non-core grant as client credentials merely because it has no browser.
Scopes answer “what”; audiences answer “where”
OAuth scope values are case-sensitive strings defined by the authorization server. They describe requested access and are separated by spaces. The server can grant less than the client requested. If the issued scope differs from the requested scope, the response must report the issued scope.
An audience restriction limits the resource where an access token can be used. RFC 8707 defines the resource parameter so a client can identify a target protected resource and let the authorization server apply an audience restriction.
GET /authorize?response_type=code
&client_id=reporting-ui
&scope=orders%3Aread
&resource=https%3A%2F%2Forders.example%2F
Here, orders:read says what access is requested. https://orders.example/ identifies where the resulting access is intended to be used. The authorization server can map the resource identifier to an audience value.
Wrong "A token with admin scope can be accepted by every company API."
Right Permission and recipient are separate checks. An API should not accept a token intended for a different audience.
RFC 8707 encourages a single resource parameter when feasible. A bearer token valid for multiple audiences can be used by any receiving resource at the others, so those resources need a high degree of trust.
OIDC scopes request identity claims
Including openid makes a request an OpenID Connect request. OIDC also defines profile, email, address, and phone scope values that request sets of claims. offline_access requests access after the user is no longer present and can result in a refresh token. OIDC requires consent for that offline access unless other conditions for processing it are in place, and the request must use a response type that returns an authorization code.
An ID token's aud has a different immediate meaning from an API token audience. The OIDC client must find its own client_id in the ID token's audience claim. That binds the authentication statement to its intended client.
Review the whole authorization transaction
PKCE is more than adding two parameters. The challenge must be transaction-specific and securely bound to the client and user agent where the transaction started. The authorization server stores the challenge with the authorization code, then enforces the matching verifier at the token endpoint. It must also reject a downgrade where a token request supplies a verifier even though the authorization request had no challenge.
Use S256 so the authorization request does not disclose the verifier:
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
The verifier itself appears only in the later token request. PKCE protects the code redemption. It does not turn an access token into a proof-of-possession token.
Redirect URI validation remains part of the boundary. RFC 9700 requires exact string matching against pre-registered redirect URIs, with a port exception for native applications using localhost. A prefix or wildcard match can route a code to an attacker-controlled endpoint.
Wrong "PKCE makes redirect URI validation and response correlation unnecessary."
Right The server still validates redirect URIs. The client also needs a CSRF defense that correlates the response to its browser session. RFC 9700 recognizes PKCE, OIDC nonce, and the long-established random state value as protections under their stated conditions.
Validate the token for its job
An access token is input to a resource server. Its format is outside core OAuth, and RFC 6749 says it is usually opaque to the client. A deployment that uses JWT access tokens still has to validate the token under the applicable access-token profile and authorization policy. A decodable JWT is not automatically an acceptable API credential.
An OIDC ID token is a JWT with a defined validation procedure for the client. For an authorization-code response, that includes checking the issuer exactly, finding the client's client_id in aud, validating the token's cryptographic protection according to the OIDC rules, and checking that the current time precedes exp. If the authentication request sent a nonce, the ID token must contain the same value and the client checks it.
ID token consumer: the OIDC client identified by client_id in aud
Access token consumer: the protected resource selected by its audience restriction
Refresh token consumer: the authorization server's token endpoint
Wrong "Signature verification is enough because all three tokens are JWTs."
Right OAuth does not require access or refresh tokens to be JWTs. For a JWT, a valid signature establishes integrity from a key, but the consumer still enforces issuer, audience, time, token purpose, and the authorization relevant to that token.
Bound access by capability and recipient
Scopes are authorization-server-defined, case-sensitive access ranges. They do not identify a resource unless a particular deployment defines them that way. RFC 8707 separates the target into the resource parameter so the authorization server can issue audience-restricted access tokens.
For two APIs, request tokens with narrow scope and one target resource where feasible:
# Token intended for the orders API
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=ROTATING_TOKEN&scope=orders%3Aread&resource=https%3A%2F%2Forders.example%2F
The acceptable resource and scope on a refresh request remain subject to server policy and the original grant. RFC 8707 permits a subset of originally granted resources. RFC 6749 forbids a refresh request from adding scope that was not originally granted.
Refresh tokens widen the incident window
Refresh tokens are credentials for obtaining more access tokens, so RFC 6749 requires confidentiality in transit and storage and binding to the client. RFC 9700 adds a replay-detection requirement for public clients: their refresh tokens must be sender-constrained or use rotation.
With rotation, the authorization server issues a new refresh token and invalidates the previous one while retaining information about their relationship. Reuse of an invalidated token can reveal that the credential was copied. When a response returns a new refresh token, the client discards the old value:
refresh_token_v1 -> access_token + refresh_token_v2
discard refresh_token_v1
Do not promise perpetual sessions. RFC 9700 says authorization servers should expire refresh tokens after inactivity and may revoke them after events such as password change or logout. The client must handle a failed refresh by returning to an authorization flow instead of repeatedly presenting the rejected credential.
The password grant remains prohibited by the current BCP. The implicit grant is also a poor default because its access token appears in the authorization response. Neither becomes safer by wrapping the resulting token in a JWT.
dig deeper
Primary sources behind these notes - the specs and official docs worth reading in full.
- IETF: The OAuth 2.0 Authorization Framework ↗rfc-editor.org
- IETF: Proof Key for Code Exchange by OAuth Public Clients ↗rfc-editor.org
- IETF: OAuth 2.0 Security Best Current Practice ↗rfc-editor.org
- IETF: Resource Indicators for OAuth 2.0 ↗rfc-editor.org
- OpenID Foundation: OpenID Connect Core 1.0, Errata Set 2 ↗openid.net
beta
The interviewer part is in the works.
The diagnostic, personal maps, and AI mock interviews are being finished right now. The notes stay free either way. Leave an email and you'll get the first-cohort invite, plus a month of Pro when it opens.