Skip to content
Discovery Authentication 4 min read · Updated 26 Jun 2026

How OAuth 2.0 Really Works

intermediate securityoauthtokens

The single most common misunderstanding about OAuth 2.0 is what it is for. It is an authorisation framework. It answers “may this application read this user’s repositories?” — not “who is this user?”. Those look similar from the outside because “Sign in with GitHub” is built on top of OAuth, but the login part is a separate layer bolted on afterwards, and conflating the two is how insecure integrations get written.

The problem OAuth solves is concrete. Before it, an app that wanted to read your calendar asked for your calendar password. That gave it everything, forever, with no way to see what it was doing and no way to revoke it short of a password change. OAuth replaces the password with a scoped, expiring, revocable token.

The four roles

  • Resource owner — the human who owns the data.
  • Client — the application that wants access.
  • Authorisation server — issues tokens after the owner consents.
  • Resource server — the API holding the data, which validates tokens.

The authorisation server and resource server are frequently the same company but are almost always different services, and keeping them conceptually separate is what makes the token format decisions later make sense.

The authorisation code flow, step by step

This is the flow. Everything else is either a special case or deprecated.

1. Redirect. The client sends the browser to the authorisation server with a description of what it wants.

GET https://auth.example.com/authorize
  ?response_type=code
  &client_id=abc123
  &redirect_uri=https://app.example.com/callback
  &scope=repo:read
  &state=9f2c7a
  &code_challenge=E9Melhoa2Ow...
  &code_challenge_method=S256

state is an opaque random value the client stores in the user’s session. It comes back unchanged and must be compared — that comparison is what prevents CSRF against the redirect endpoint, where an attacker tricks a victim’s browser into completing the attacker’s authorisation and silently linking accounts.

2. Consent. The user authenticates directly with the authorisation server and sees the requested scopes. The client never touches these credentials. That is the entire point of the redirect.

3. Code. The browser is redirected back with a short-lived, single-use code: https://app.example.com/callback?code=SplxlOB&state=9f2c7a. The code travels through the browser’s URL bar, which means it can leak — into logs, into a Referer header, into browser history. So it is deliberately useless on its own.

4. Exchange. The client swaps the code for tokens on a direct back-channel POST, proving it is the same client that started the flow.

POST /token
grant_type=authorization_code
&code=SplxlOB
&redirect_uri=https://app.example.com/callback
&client_id=abc123
&code_verifier=dBjftJeZ4CVP...

The response carries an access_token, an expires_in, usually a refresh_token, and — if OpenID Connect is in play — an id_token.

PKCE, and why the implicit flow died

Step 1 sent a code_challenge: the SHA-256 hash of a random code_verifier the client generated and kept. Step 4 sends the verifier itself. The authorisation server hashes it and checks it matches. So a stolen authorisation code is worthless to anyone who does not also hold the verifier.

This is Proof Key for Code Exchange, and it was originally designed for mobile apps where a malicious app could register the same custom URL scheme and intercept the redirect. Current guidance applies it to every client, including server-side ones with a client secret — defence in depth costs nothing here.

PKCE is also why the implicit flow (response_type=token, returning an access token directly in the URL fragment) is gone. It existed because browsers could not make cross-origin token requests before CORS was universal. It put a real credential in a URL, could not deliver refresh tokens safely, and had no binding between requester and receiver. It is deprecated in the current security best practice, and so is the resource owner password credentials grant, which had the client collecting the user’s password — the exact thing OAuth was invented to stop.

Where login actually comes from

OpenID Connect is a thin layer on top of OAuth 2.0 that adds identity. Request the openid scope and you additionally receive an id_token: a JWT, signed by the authorisation server, containing sub (the stable user identifier), iss, aud, exp and optionally profile claims.

Validate it properly — signature against the provider’s published JWKS, iss matches the expected issuer, aud matches your client id, exp is in the future, and the nonce matches the one you sent. An id_token you did not verify is a string a stranger gave you.

And note the asymmetry: the id_token is for your application, the access_token is for the API. Sending an id_token to a resource server, or treating an opaque access_token as proof of identity, are the two mistakes that show up most often in code review.

Quick answers

What is OAuth 2.0 in simple terms?
A protocol for letting one application act on a user's behalf in another, without the user handing over their password. The user approves a scoped grant at the provider, and the application receives a token that works only for what was approved.
What is the difference between OAuth and OpenID Connect?
OAuth 2.0 is authorization — it decides what an application may do. OpenID Connect is a thin identity layer on top that adds an ID token, so it also tells you who the user is. Using a raw OAuth access token as proof of identity is a well-known mistake.
Why is the implicit flow no longer recommended?
It returned the access token directly in the URL fragment, where it lands in browser history, referrer headers and logs, and it offered no way to authenticate the client. Authorization code with PKCE replaces it for browser and mobile apps.
What problem does PKCE solve?
It stops an attacker who intercepts the authorization code from exchanging it. The client sends a hash of a secret it generated, and must present the original secret at the token exchange — so a stolen code alone is useless.

References

Related Discoveries