OAuth 2.0
OAuth 2.0
Delegated authorization for HTTP APIs: who the participants are, which grants still belong in new systems, and what OAuth is not.
OAuth 2.0 is a framework for delegated authorization: it lets an application obtain limited access to an HTTP API, either on behalf of a person who approves that access or on its own behalf, without ever handling that person's password. It was published as RFC 6749 in October 2012, with bearer token usage defined separately in RFC 6750. Its current security baseline is RFC 9700, the OAuth 2.0 Security Best Current Practice (BCP 240, January 2025), which updates RFC 6749, RFC 6750 and RFC 6819.
This page explains the moving parts: who the participants are, which grant types exist and which are now deprecated, which endpoints are involved, and how clients are classified. The flow pages that follow go into each grant in detail.
What OAuth is, and what it is not
The problem OAuth solves is narrow and important. Before it, a third-party application that wanted to read your calendar typically asked for your password and stored it. That gave the application all of your access, forever, with no way to revoke it short of changing the password. OAuth replaces the password with an access token: a credential that represents a specific, limited grant, issued by a server you trust, which can expire and be revoked independently.
OAuth is not an authentication protocol. An access token tells an API that some client was authorized to do something; it is not designed to tell the client who the user is. RFC 6749 leaves the format of access tokens undefined, does not define any claims about the user, and says nothing about how the client should learn the user's identity. Applications that "log in with OAuth" by calling some profile API with an access token are building their own, unstandardized identity layer, and several well-known attacks (token substitution, accepting a token issued to a different client) come from exactly that.
The four roles
RFC 6749 §1.1 defines four roles. One piece of software can play more than one of them, but keeping them separate in your head prevents most design mistakes.
- Resource owner
- The entity that can grant access to a protected resource. When it is a person, the RFC calls it the end-user.
- Client
- The application that makes requests to the API on the resource owner's behalf and with their authorization. "Client" says nothing about where it runs: a server-side web app, a single-page app in a browser, a mobile app and a batch job are all clients. See client.
- The server that authenticates the resource owner, obtains their authorization, and issues access tokens to the client. See authorization server.
- Resource server
- The API that hosts the protected resources and accepts access tokens. See resource server.
RFC 6749 deliberately leaves the relationship between the authorization server and the resource server out of scope: they may be the same service or separate ones, and one authorization server may issue tokens for many APIs. In practice the resource server either validates a self-contained token (for example a JWT access token, RFC 9068) or asks the authorization server about an opaque one through token introspection. The tokens page covers both.
Grant types and their status today
A grant is the credential the client presents to the token endpoint to get an access token. RFC 6749 defines four, plus an extension mechanism (§4.5) that later specifications used for the device grant and others. Two of the original four are now deprecated.
| Grant | Defined in | RFC 9700 (BCP 240) | OAuth 2.1 draft | Use it for |
|---|---|---|---|---|
| Authorization code | RFC 6749 §4.1 | Recommended; public clients MUST use PKCE, confidential clients RECOMMENDED (§2.1.1) | Kept; PKCE built in | Any app acting for a user: web, single-page, mobile, desktop |
Implicit (response_type=token) | RFC 6749 §4.2 | Clients SHOULD NOT use it (§2.1.2) | Omitted | Nothing new. Migrate to code + PKCE |
| Resource owner password credentials | RFC 6749 §4.3 | MUST NOT be used (§2.4) | Omitted | Nothing. It hands the user's password to the client |
| Client credentials | RFC 6749 §4.4 | Not deprecated | Kept | Machine-to-machine calls with no user |
| Refresh token | RFC 6749 §6 | Public clients' refresh tokens MUST be sender-constrained or rotated (§2.2.2) | Kept | Getting new access tokens without asking the user again |
| Device authorization | RFC 8628 | Not deprecated | Separate RFC | TVs, consoles, command-line tools without a usable browser |
Note the different strengths. The resource owner password credentials grant gets an unconditional MUST NOT: it exposes the user's credentials to the client, widens the places a password can leak, trains users to type passwords into third-party screens, and cannot accommodate multi-step or phishing-resistant authentication. The implicit grant gets SHOULD NOT, with an exception "unless access token injection in the authorization response is prevented and the aforementioned token leakage vectors are mitigated". In practice that exception is rarely worth pursuing, because the authorization code grant with PKCE works everywhere the implicit grant did.
The resource owner password credentials grant [RFC6749] MUST NOT be used.
OAuth 2.1 is an IETF Internet-Draft, not an RFC. The version current at the time of writing is draft-ietf-oauth-v2-1-16 (September 2026). It consolidates RFC 6749, RFC 6750 and the later best practices into one document: it omits the implicit and password grants, requires PKCE with the authorization code grant, removes the PKCE plain method, requires exact redirect URI matching, drops bearer tokens in query strings, and drops the redirect_uri parameter from the token request. Until it is published as an RFC, cite RFC 6749 plus RFC 9700 as the normative baseline and treat the draft as a strong signal of direction.
Endpoints
RFC 6749 §3 defines two endpoints on the authorization server and one on the client. Extensions add more.
| Endpoint | Lives at | Called by | Purpose |
|---|---|---|---|
| Authorization endpoint | Authorization server | The user's browser (front channel) | Authenticate the user, get consent, return a code. RFC 6749 requires the server to support GET. |
| Token endpoint | Authorization server | The client directly (back channel) | Exchange a grant for tokens. The client MUST use POST (§3.2). |
| Redirection endpoint | Client | The user's browser | Receives the authorization response. It MUST be an absolute URI without a fragment (§3.1.2). |
| Revocation endpoint | Authorization server | Client | Invalidate a token (RFC 7009). |
| Introspection endpoint | Authorization server | Resource server | Ask whether a token is active and what it allows (RFC 7662). |
| Device authorization endpoint | Authorization server | Device client | Start the device flow (RFC 8628). |
| Pushed authorization request endpoint | Authorization server | Client | Send authorization parameters directly instead of through the browser (RFC 9126). |
The split between front channel and back channel is the key security idea. Anything that travels through the browser (the authorization request and response) can be observed, logged, modified or replayed by an attacker who controls or watches the browser. That is why the authorization code is short-lived and single-use, and why tokens are delivered on the back channel to the token endpoint, where the client can authenticate and prove possession of the PKCE verifier.
Client types
RFC 6749 §2.1 divides clients into two types by one question: can the client keep a credential secret?
- Confidential clients can, for example a web application whose code and secrets live on a server the user cannot read.
- Public clients cannot: native apps installed on the user's device and browser-based apps, whose code and any embedded secret are available to anyone who looks.
The distinction matters because a public client's client_id identifies the software, not a proven instance of it. RFC 6749 §2.3 says the authorization server "MUST NOT rely on public client authentication for the purpose of identifying the client", and §2.2 says the client identifier "is not a secret" and "MUST NOT be used alone for client authentication". A secret shipped inside a mobile app is not a secret. RFC 8252 makes the same point for native apps: unless a mechanism such as dynamic registration gives each installation its own credential, native apps are public clients and MUST be registered as such (RFC 8252 §8.4).
The OAuth 2.1 draft keeps the same two types but phrases them by whether the client has credentials with the authorization server, and recommends a backend-for-frontend pattern for browser apps that want to use client credentials.
Discovery with authorization server metadata
RFC 8414 defines a JSON document, served at /.well-known/oauth-authorization-server under the issuer identifier, that describes an authorization server's endpoints and capabilities. RFC 9700 §2.6 RECOMMENDS that authorization servers publish it and that clients use it to configure themselves, because hand-copied endpoint URLs are a source of misconfiguration and of the mix-up attacks it describes.
GET /.well-known/oauth-authorization-server HTTP/1.1
Host: as.example.com
HTTP/1.1 200 OK
Content-Type: application/json
{
"issuer": "https://as.example.com",
"authorization_endpoint": "https://as.example.com/authorize",
"token_endpoint": "https://as.example.com/token",
"jwks_uri": "https://as.example.com/jwks.json",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["private_key_jwt", "tls_client_auth"],
"revocation_endpoint": "https://as.example.com/revoke",
"introspection_endpoint": "https://as.example.com/introspect",
"authorization_response_iss_parameter_supported": true
}
Two rules make the document trustworthy. The issuer value MUST be identical to the issuer identifier the client used to build the metadata URL, and if it is not, the data MUST NOT be used (RFC 8414 §3.3). And the code_challenge_methods_supported field is how RFC 9700 expects servers to advertise PKCE support. OpenID Connect providers publish an equivalent document at /.well-known/openid-configuration; see OpenID Connect.
What to do
- For any app acting on behalf of a user, use the authorization code grant with PKCE and
S256, whether the client is public or confidential. - Remove the password grant. RFC 9700 says it MUST NOT be used; there is no "internal apps" exception in the text.
- Plan the retirement of any implicit-grant clients; nothing needs it any more.
- Use client credentials only for confidential, machine-to-machine clients, and prefer asymmetric client authentication (RFC 9700 §2.5 RECOMMENDED).
- Register exact redirect URIs; RFC 9700 §2.1 requires authorization servers to use exact string matching (except the port of loopback redirects for native apps).
- Publish and consume RFC 8414 metadata, and validate
issuer. - Treat OAuth as authorization only. For sign-in, use OpenID Connect.
- Read the plain-English summary of the full BCP: OAuth security mistakes.
Sources
- RFC 6749: The OAuth 2.0 Authorization Framework, IETF. IETF Trust Legal Provisions (brief quotations with attribution).
- RFC 9700: Best Current Practice for OAuth 2.0 Security, IETF. IETF Trust Legal Provisions (brief quotations with attribution).
- The OAuth 2.1 Authorization Framework, draft-ietf-oauth-v2-1-16 (Internet-Draft, work in progress), IETF. IETF Trust Legal Provisions (brief quotations with attribution).
- RFC 8414: OAuth 2.0 Authorization Server Metadata, IETF. IETF Trust Legal Provisions (brief quotations with attribution).
- RFC 8252: OAuth 2.0 for Native Apps, IETF. IETF Trust Legal Provisions (brief quotations with attribution).
- RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage, IETF. IETF Trust Legal Provisions (brief quotations with attribution).