OAuth 2.0
OAuth 2.0 is a delegated authorisation protocol: it lets an application act on a resource owner's behalf without ever seeing their password. It is not an authentication protocol — OpenID Connect is the layer added on top for that.
Roles
| Role | Who it is |
|---|---|
| Resource owner | The user who owns the data |
| Client | The application requesting access |
| Authorization server | Authenticates the owner and issues tokens |
| Resource server | Holds the protected data and accepts tokens |
Abstract flow
From RFC 6749 §1.2:
Client ──(1) Authorization Request──▶ Resource Owner
Client ◀─(2) Authorization Grant──── Resource Owner
Client ──(3) Authorization Grant───▶ Authorization Server
Client ◀─(4) Access Token─────────── Authorization Server
Client ──(5) Access Token──────────▶ Resource Server
Client ◀─(6) Protected Resource───── Resource Server
The grant in steps 2 and 3 is an intermediate credential. Its type is what distinguishes the flows below.
Grant types
Authorization code
The default for anything with a user. The code is returned through the browser; the token is fetched over a direct back-channel call, so it never appears in a URL or in browser history.
Resource Owner ◀── redirect to authorization server ── Client
Resource Owner ──── authenticates ─────────────────▶ Authorization Server
Resource Owner ◀─── authorization code ───────────── Authorization Server
Resource Owner ──── authorization code ────────────▶ Client
Client ──── code + client credentials ─────▶ Authorization Server
Client ◀─── access token ─────────────────── Authorization Server
Always add PKCE (Proof Key for Code Exchange, RFC 7636). The client sends a
hash of a random code_verifier when requesting the code, then the verifier
itself when exchanging it. An attacker who intercepts the code cannot use it
without the verifier.
Originally a mobile-app countermeasure, PKCE is now recommended for every client type including confidential server-side ones, and is mandatory in OAuth 2.1.
code_challenge = BASE64URL(SHA256(code_verifier))
Client credentials
Machine to machine. There is no user, so nothing to redirect and no consent.
Client ──── client id + secret ────▶ Authorization Server
Client ◀─── access token ─────────── Authorization Server
Use this for service-to-service calls, scheduled jobs and backend integrations. The token represents the client itself, so it carries no user identity — do not build per-user authorisation on top of it.
Resource owner password credentials
The client collects the username and password directly and exchanges them for a token.
Resource Owner ──── username + password ────▶ Client
Client ──── username + password ────▶ Authorization Server
Client ◀─── access token ──────────── Authorization Server
Deprecated. It defeats the point of OAuth by exposing the password to the client, it cannot support multi-factor authentication or federated login, and it is removed in OAuth 2.1. Spring Authorization Server does not implement it. Use the authorization code flow with PKCE instead, even for first-party applications.
Implicit
The token was returned directly in the redirect URI fragment, skipping the code exchange.
User Agent ──── authorization request ─────▶ Authorization Server
User Agent ◀─── authenticates ────────────── Authorization Server
User Agent ◀─── redirect with token in fragment ── Authorization Server
User Agent ──── request resource ──────────▶ Resource Server
Deprecated. The token is exposed in the URL, in browser history and in referrer headers, and there is no client authentication. It existed because browsers once could not make cross-origin requests to the token endpoint; CORS solved that years ago. Single-page applications should now use the authorization code flow with PKCE.
Refresh token
Exchanges a long-lived refresh token for a new access token without involving the user. Issue refresh tokens only to clients that can keep them confidential, and rotate them on each use for public clients.
Client ID and secret
When a developer registers an application, the authorization server issues a client ID and, for confidential clients, a secret.
Client ID
A public identifier, unique across every client the authorization server knows. Being public does not mean it should be guessable — a predictable ID makes it easier to craft convincing phishing attacks against a specific application. A 32-character hex string is a common choice.
Real examples of the shapes services use:
GitHub: 6779ef20e75817b79602
Google: 292085223830.apps.googleusercontent.com
Client secret
Effectively the application's own password, known only to the client and the authorization server.
- Generate it from a cryptographically secure source — 256 bits of randomness, hex- or base64url-encoded. Avoid general-purpose UUID libraries, some of which derive values from the timestamp and MAC address.
- Make it visually distinct from the ID, so a developer copying both can tell
them apart. A longer string, or a
secret_prefix, is enough. - Never ship one in a public client. Mobile apps and browser-based applications cannot keep a secret: anything in the binary or the bundle is extractable. Those are public clients and must use PKCE instead.
Storage
The pair is equivalent to a username and password, so store the ID in the clear and the secret hashed — never in plain text.
Displaying the secret only once at creation, and storing only its hash, is the strongest option and increasingly the norm. If secrets can be retrieved later, put a re-authentication prompt in front of that screen.
Spring Authorization Server expects the stored secret to be encoded with the
application's PasswordEncoder:
RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client-demo")
.clientSecret(passwordEncoder.encode("secret"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:8080/authorized")
.scope("message.read")
.build();
A secret stored unencoded produces an invalid_client error at the token
endpoint, with nothing in the log pointing at the encoding as the cause.
The client ID and secret guidance above follows the recommendations published at oauth.com.
Related
- OAuth 2.0 Authorization Server — running one with Spring.
- JWT — the usual access token format.
- Client credential handling — PKI and signing more broadly.