Skip to main content

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

RoleWho it is
Resource ownerThe user who owns the data
ClientThe application requesting access
Authorization serverAuthenticates the owner and issues tokens
Resource serverHolds 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.