Origins: authorisation was never meant to log anyone in
was written for a specific problem, and identity was not it. Around 2007 the pattern for connecting one service to another was password sharing: a user handed a site their email credentials so it could read their contacts. OAuth 1.0 existed to end that, to let an application obtain delegated access to a resource without receiving the user's password.
OAuth 2.0 ( 6749, 2012) simplified and generalised it, and it says so plainly: it is an authorisation framework. It has no defined way to say who the user is, no standard shape for a user identifier, and no required proof of when or how the user authenticated.
That did not stop anyone. Providers built login on top of it anyway, each in a different way, and the era of proprietary "sign in with" flows produced the predictable class of vulnerabilities, most memorably the confused deputy: an access token proves an application may call an API, not who is sitting at the keyboard, so an application that accepts a token as proof of identity can be handed a token obtained elsewhere, for a different user, and be fooled.
OpenID Connect (2014) is the correction. It is a thin identity layer on OAuth 2.0, not a replacement: same endpoints, same flows, plus the pieces authentication actually needs: an ID token as a signed with defined claims, a UserInfo endpoint, a discovery document at a well-known URL, and published signing keys.
The lineage matters because it explains the shape: did not redesign anything. It added the missing statement, this user authenticated, at this time, with this method, at this issuer, for this audience, to a protocol that had deliberately left it out.
Two questions, not one
OAuth 2.0 and OpenID Connect are constantly confused, partly because OIDC (OpenID Connect) is built on OAuth and they share endpoints and terminology. The cleanest way to keep them apart is to notice that they answer different questions. OAuth 2.0 answers an authorization question: is this application allowed to access that resource on the user's behalf. OpenID Connect answers an authentication question: who is the user that just signed in. One is about permission to act; the other is about identity. OIDC layers the identity answer on top of OAuth's permission machinery.
Access token versus ID token
The clearest expression of the difference is the two kinds of token, and treating them interchangeably is the most common and most dangerous mistake.
An access token is a credential for calling an API. It represents a delegated permission, and the application is meant to send it to a resource server, which checks it and serves the request. To the client application, an access token is frequently opaque: the client is not the intended audience and is not supposed to parse it or make decisions from its contents. Its meaning belongs to the resource server.
An ID token is a statement about the user, issued by the provider to the client. The client is the intended audience, and the client is supposed to read it, validate it, and learn who the user is. It is always a JWT with a defined set of claims. The audience claim is the tell: an ID token's audience is the client, while an access token's audience (when it has one) is the resource server. Using an access token to decide who the user is, or feeding an ID token to an API as if it were an access token, are both category errors that lead to real vulnerabilities.
Why plain OAuth is not a login protocol
Before OIDC existed, many applications implemented "log in with provider X" using raw OAuth 2.0, and the pattern persists in places. The application would obtain an access token and treat the ability to obtain it, or the result of calling some user-info API with it, as proof of who the user is. This is a known antipattern, and the reason is subtle but important: an access token says nothing reliable about who requested it or who it was issued to. A token obtained by one application can, in some configurations, be presented by another, and an application that infers identity from mere possession of an access token can be fooled into logging in the wrong user. This is sometimes called the confused deputy problem.
OIDC was designed to close exactly this gap. The ID token is explicitly addressed to a specific client through its audience claim, is bound to a specific login attempt through its nonce, and is signed by the provider so it cannot be forged. Those are precisely the properties an access token lacks and that authentication requires. The practical rule is simple: do not build login on raw OAuth. If you need to know who the user is, use OpenID Connect and validate the ID token.
The claims that do the work
An ID token is only useful if it is validated, and the validation is the protocol:
iss: the issuer, checked against the one you configured, not the one the token names.aud: the audience, which must be your client identifier. This is the check that stops the confused deputy.expandiat: expiry and issued-at, with allowance for clock skew.nonce: bound to the value your application sent, which stops a token being replayed into a different session.sub: the stable identifier for the user at that issuer. Not the email address, which can change and can be reassigned; the pairing that is actually stable is issuer plus subject.auth_time,acr,amr: when the user authenticated and by what means, which is how an application requires a fresh or a stronger login for a sensitive action.
And the signature, verified against the issuer's published keys, with the key rotation that implies.
The flows, and which ones are still correct
- Authorization code with : the current answer for every client type, browser or native. PKCE began as a mobile fix and is now recommended universally, because it binds the code to the client that requested it.
- Implicit flow: returned tokens straight through the browser. Deprecated: tokens in URLs land in histories, logs and referrers.
- Resource owner password credentials: the user hands their password to the application. Removed from current guidance, since it recreates exactly the problem OAuth was invented to solve.
- Client credentials: no user at all, service to service. Correct, and outside the identity question.
- Device authorisation: for input-constrained devices, where the user authenticates on a second screen.
Front-channel and back-channel logout are the parts most deployments skip, and the reason a user who signs out of one application remains signed in to five others.
The ecosystem, by category
- Identity providers: Okta, Microsoft Entra, Ping Identity, Auth0, Google, and the open-source Keycloak. What differs between them is rarely the protocol, which is well specified; it is administration, token lifetimes, session handling and how they federate.
- Libraries: the honest guidance is to use a certified one rather than assemble the validation yourself, because every step in the list above is a place where a plausible-looking implementation is silently insecure.
- API gateways and proxies, which validate tokens at the edge so services behind them do not each reimplement it.
- The certification programme run by the OpenID Foundation, which is the practical way to know an implementation actually conforms rather than merely interoperates on a good day.
remains widespread in enterprise and education, and the choice between them is usually institutional rather than technical: SAML where the counterparty is an established enterprise federation, OIDC where the clients are mobile applications and modern APIs. Both authenticate; they differ in encoding, in transport and in how much of the browser they involve.
Telling them apart in practice
When you have a token in hand and are not sure which it is, a few signals help. If it decodes as a JWT with iss, sub, aud, exp, and iat, and the audience is your client identifier, it is an ID token; the OIDC decoder will recognize it and interpret the claims. An access token may be opaque (not a JWT at all), or it may be a JWT whose audience is a resource server and whose claims describe scopes and permissions rather than user identity. The presence of the openid scope in the original request is what caused an ID token to be issued in the first place. When in doubt, decode the token and look at the audience and the claims: they tell you what the token is for, and therefore how it should be used.
This series, in order
This article opens the series OAuth and OpenID Connect. It separates the two jobs that are constantly confused; the parts build OAuth first (the code flow, who can keep a secret, PKCE, the tokens, the grant to pick) and then the identity layer on top of it (what OpenID Connect adds, its flow, the claims a relying party checks, and how a provider describes itself).
- The OAuth 2.0 authorization code flow. The four roles, the redirect and the exchange, and why the code is swapped for a token on the back channel.
- Public vs confidential clients, and where PKCE fits. Whether a client can keep a secret decides its security model, and why single-page and mobile apps are public clients.
- PKCE: securing the OAuth authorization code flow. The interception attack PKCE defeats, the verifier and the challenge, and why S256 is mandatory.
- Access tokens, refresh tokens, and ID tokens. Access, refresh and ID tokens, what each is for, and the real bug of sending one where another belongs.
- OAuth flows: choosing the grant in 2026. Code with PKCE for people, client credentials for machines, the device grant for televisions, and why the implicit grant is retired.
- OpenID Connect: An Identity Layer on OAuth 2.0. The identity layer: the ID token at its centre, the relying party and provider roles, and why an ID token is a JWT.
- The OIDC Authorization Code Flow. How an app receives an ID token: the login at the provider, the short-lived code, the back-channel exchange.
- The ID Token Claims, and What a Relying Party Checks. iss, sub, aud, exp and iat, the nonce that stops replay, azp, acr and amr: what a relying party checks.
- OIDC Discovery: The openid-configuration Document. The .well-known/openid-configuration document: endpoints, jwks_uri, signing algorithms, learned automatically.