OAuth 2.0 Overview
authsoftware

OAuth 2.0 Overview


I keep having to look up the same things in RFC 6749 — the names of the endpoints, which parameters go on which one, and which grant types still matter. So this is my own cheat sheet, mostly for myself.

The three endpoints

OAuth defines three endpoints. The first two live on the authorization server, the third is on the client.

  • Authorization endpoint — where the user is redirected to sign in and approve access. Takes response_type, client_id, redirect_uri, scope and state.
  • Token endpoint — the back-channel endpoint the client calls to exchange a grant for an access token. Takes grant_type, code, redirect_uri, client_id and, for confidential clients, client_secret.
  • Redirection endpoint — the client's URL that the authorization server sends the user back to. It doesn't take parameters itself, but it receives them in the query string: code, state, or on errors error and error_description. For the implicit grant the access token comes back in the URL fragment instead.

The four grant types

The spec defines four ways to actually get a token. In practice only two of them are still worth using.

Authorization code — the one you should be using for almost everything. The user is redirected to the auth server, comes back with a code, and the client exchanges that code at the token endpoint for an access token (and optionally a refresh token). Combine with PKCE for public clients and you're in good shape.

Client credentials — used when there's no user involved. A server calling another server on its own behalf. Client authenticates itself at the token endpoint and gets an access token back. Simple.

Implicit — returns the access token directly in the URL fragment after authorization, skipping the token endpoint. It was meant for single-page apps that couldn't do a secure back-channel call, but it leaks tokens into browser history and has no refresh mechanism. Use authorization code with PKCE instead.

Resource owner password credentials — the user hands their password to the client, which forwards it to the auth server. It defeats most of the point of OAuth and should only exist in legacy migrations.

What a normal flow looks like

For the code flow, in order:

  1. Client redirects the user to /authorize with response_type=code, a client_id, a redirect_uri, the scope, and a state value it generated.
  2. User authenticates and consents on the auth server.
  3. Auth server redirects back to redirect_uri with ?code=...&state=....
  4. Client verifies state matches what it sent, then POSTs to /token with grant_type=authorization_code, the code, its client_id (and client_secret if confidential) and the same redirect_uri.
  5. Auth server returns an access token, optionally a refresh token, and the token metadata.

The state parameter is there to stop CSRF. Always generate it, always verify it. If your library doesn't do this for you, find a different library.

Response you get back

The token endpoint returns JSON like this:

{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "scope": "read write"
}

scope is only echoed back if it's different from what was requested. refresh_token only shows up if the client is allowed to have one.

That's pretty much all I reach for day to day. For the edge cases — device flow, token introspection, revocation, dynamic client registration — those live in their own RFCs and I'll write them up separately when I need them.