Library
9 min read

OAuth 2.0 flows

Published

When a photo printing app asks to read your Google Photos, it should never see your Google password. You go to Google, Google asks "let PrintKaro see your photos?", you say yes, and PrintKaro gets a token that can read photos and do nothing else. That is OAuth 2.0: a way to hand an app limited access to something of yours, without handing over your password.

A flow (the spec calls it a grant) is the sequence of redirects and requests that ends with the app holding that token. There used to be five. In 2026 you need three, and the interview question is almost always "which one, and why".

Your answer

A YouTube app on a smart TV needs to sign you in. Typing a password with a TV remote is miserable and the TV has no proper browser. How would you design the sign in?

The four roles#

  • Resource owner: you. The photos are yours.
  • Client: the app that wants access, PrintKaro.
  • Authorization server: the one that asks for your consent and issues tokens, Google's accounts service.
  • Resource server: the API holding the data, the Google Photos API. It accepts the token.

And two kinds of client, which decides everything below. A confidential client runs on a server and can keep a secret. A public client runs on a device the user controls (a browser SPA, a mobile app, a CLI) and cannot, because anyone can unpack the app and read it.

Flow 1: authorization code with PKCE#

This is the default for any app with a user and a browser: web apps, single page apps, mobile apps. Two steps make it safe. The token never travels through the browser's address bar: the browser only carries a short lived, single use code, and the app swaps the code for tokens over a direct back channel. And PKCE (Proof Key for Code Exchange, said "pixie") proves that whoever swaps the code is the same app that started the flow.

1/8 A random 43 to 128 character code_verifier, kept secret in the app. Its SHA-256 hash is the code_challenge.

Why the code and not the token directly? An old flow called implicit did exactly that: the token came back in the URL fragment. URLs end up in browser history, in logs and in Referer headers, and malicious apps on a phone could register the same custom URL scheme and catch the redirect. PKCE fixes the stolen code problem even for public clients, which is why OAuth 2.1 drops implicit entirely and requires PKCE for every authorization code flow.

Flow 2: client credentials#

No user at all. A nightly billing job needs to call the invoices API. There is nobody to ask for consent, so the service authenticates as itself, with a client id and secret (or better, a signed JWT or a client certificate), and receives a token that represents the service.

bash
curl -X POST https://auth.acme.in/oauth/token \
  -d grant_type=client_credentials \
  -d client_id=billing-job \
  -d client_secret=$BILLING_SECRET \
  -d scope=invoices:read

This is the machine to machine flow. It is only for confidential clients, since it depends on a secret. Never ship it inside a mobile app.

Flow 3: device authorization#

The smart TV problem. The device cannot show a decent browser and has a terrible keyboard, but you have a phone in your hand. The TV asks for a device code, shows you a short code and a URL, and polls while you approve on your phone.

1/6 The TV asks to start a sign in.

The same flow signs in command line tools (gh auth login, az login) and anything else with no browser. Its weakness is phishing: an attacker can start a device flow and send you the code with a convincing message. Show the device name on the approval screen, and keep the codes short lived.

The ones that are gone#

ChoiceWhat you gainWhat you payPick it when
Implicit grantOne fewer round trip, back when browsers could not make cross origin POSTs.Token in the URL, leaks through history and referrers, no way to tie it to the client.Removed in OAuth 2.1. Use authorization code with PKCE.
Resource owner password grantThe app collects the username and password and swaps them for a token.Defeats the point of OAuth: the app sees the password, and MFA and SSO cannot work.Removed in OAuth 2.1. At most for migrating a legacy first party app.
Refresh token grantSwaps a long lived refresh token for a new short lived access token.A stolen refresh token is valuable, so rotate it and detect reuse.Still current. Used alongside the other flows. See access vs refresh tokens.

Picking a flow#

Which flow?
A React single page app calling your own API
An Android app using "Sign in with Google"
A cron job that syncs orders to a warehouse API
A smart TV, a game console or a CLI
Your own mobile app shows its own username and password form and sends them to your token endpoint

In the interview#

Interviewer
Why does a mobile app need PKCE? The app already exchanges the code on a back channel.
They want the attack, not the acronym.
You
Because a mobile app cannot hold a client secret, anyone can decompile it. Without PKCE, another app on the phone that registers the same redirect scheme can catch the code and exchange it itself. With PKCE the app keeps a random verifier and only sends its hash at the start. The token endpoint wants the verifier itself, which the attacker never saw. So the code alone is worthless.
States the threat (redirect interception), then why the hash closes it.
Interviewer
And why not just return the token in the redirect?
You
That is the implicit flow. Tokens in URLs leak into history, logs and referrer headers, and there is nothing binding the token to the client. OAuth 2.1 removed it. The code is single use and short lived, and the real token only moves over a direct TLS connection.
Knowing it was removed, and why, beats knowing it exists.
Follow ups to have ready

"What is the state parameter for?" A random value tied to the user's session, checked on the callback, so an attacker cannot make you complete a login they started (login CSRF). "What is a scope?" A named permission the user consented to, carried in the token, checked by the API. "Does OAuth tell you who the user is?" No, that is OIDC's ID token.

Checkpoint#

Checkpoint

1. What does PKCE protect against?

2. A backend job with no user needs to call an internal API. Which flow?

3. Why did OAuth 2.1 remove the implicit flow?

Worth memorising
  • OAuth gives an app limited access without your password. It is authorization, not login.
  • Authorization code + PKCE for anything with a user and a browser, public or confidential.
  • Client credentials for machine to machine. Device code for TVs and CLIs.
  • Implicit and password grants are removed in OAuth 2.1.
  • state stops forged callbacks, PKCE stops stolen codes, scopes limit the token.
Say this in 60 seconds

OAuth 2.0 lets an app get limited access to a user's data without the user's password. The app redirects the user to an authorization server, the user consents to specific scopes, and the app gets an access token the resource server accepts. For any app with a user and a browser, including SPAs and mobile apps, I use the authorization code flow with PKCE: the browser only carries a single use code, and the app swaps it for tokens on a back channel while proving, with a verifier whose hash it sent at the start, that it started the flow. For machine to machine calls with no user I use client credentials. For devices without a usable browser, like TVs and CLIs, I use the device flow, where the user approves on their phone. Implicit and password grants are removed in OAuth 2.1 because they leak tokens or expose passwords. And OAuth alone is not login; for identity I add OpenID Connect.