Skip to main content

Command Palette

Search for a command to run...

The OAuth Mental Model

Updated
5 min readView as Markdown

Four roles (memorize these)

OAuth names four actors. Everything else hangs off them.

flowchart TB
    RO["Resource Owner<br/>(the user)"]
    AS["Authorization Server<br/>(Keycloak, Okta, Google, Auth0…)"]
    RS["Resource Server<br/>(API)"]
    CL["Client<br/>(your app)"]

    RO -->|"grants consent / trust"| AS
    RO -->|"owns resources"| RS
    AS -->|"issues access token"| RS
    CL -->|"presents access token"| RS
    CL -->|"requests authorization"| AS
Role Plain English Example
Resource owner User who can grant access Alice
Client App requesting access Your Spring Boot app
Authorization server Issues tokens after consent Keycloak realm
Resource server API that accepts tokens Your /api/orders service

One company often runs both authorization server and resource server (Google issues tokens and hosts Gmail API). In microservices, they are usually separate.


Three token types you will meet

Access token

  • Purpose: Prove authorization to call an API
  • Audience: Resource server
  • Format: Often opaque or JWT
  • Lifetime: Short (minutes to an hour)
GET /api/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Refresh token

  • Purpose: Obtain a new access token without re-prompting the user
  • Audience: Authorization server only
  • Lifetime: Longer (days/weeks), revocable
  • Storage: Server-side or secure httpOnly cookie — never localStorage in a browser

ID token (OIDC, not pure OAuth)

  • Purpose: Identity claims about the authenticated user
  • Audience: Client application
  • Format: JWT
  • Lifetime: Short; not an API credential

Rule of thumb: Use the access token for APIs. Use the ID token for login/session identity in your client.


Scopes: the valet key notches

Scopes limit what a token can do. They are strings agreed between client and authorization server.

openid profile email
read:orders write:orders
Scope Meaning
openid OIDC request — return an ID token
profile Name, picture, etc.
email Email address
read:orders Custom API scope (you define in Keycloak)

The authorization server shows the user a consent screen listing scopes. The issued access token embeds (or maps to) only approved scopes.


Flows = how the client gets tokens

A grant type (flow) is the protocol path from "user wants to use app" to "client holds tokens."

Flow Use today? Notes
Authorization Code + PKCE Yes — default SPAs, mobile, server apps
Client credentials Yes Machine-to-machine, no user
Device code Yes TVs, CLI devices
Implicit No Replaced by Auth Code + PKCE
Resource owner password No Deprecated; bypasses consent

A later post in this series walks through Authorization Code + PKCE in detail.


OIDC sits on top of OAuth

flowchart LR
    O["OAuth 2.0"]
    OIDC["OpenID Connect"]
    O -->|"authorization"| Q1["Can this app access that API?"]
    OIDC -->|"identity layer"| Q2["Who is the user?"]
    OIDC -.->|"built on"| O

OIDC adds:

  • id_token JWT
  • /userinfo endpoint
  • Discovery document at /.well-known/openid-configuration

When people say "OAuth login," they almost always mean OIDC.


Sessions vs tokens (where confusion starts)

Concept Where it lives Typical use
OAuth tokens Client + auth server contract API access
App session Your server or cookie "Logged in to our app"

A common pattern:

  1. User completes OIDC flow → your backend receives tokens
  2. Backend validates ID token, creates app session (cookie)
  3. Backend stores refresh token server-side
  4. Browser only sees session cookie; never holds refresh token

Your app still needs session management. OAuth does not replace it.


Go deeper

JWT access tokens vs opaque tokens

JWT (self-contained):

  • Resource server can validate locally (signature + claims)
  • Harder to revoke instantly
  • Must keep claims small

Opaque:

  • Resource server introspects with auth server
  • Revocation is immediate
  • Extra network hop

Keycloak can do both. Choose based on revocation needs and API topology.

Audience (aud) and issuer (iss)

JWTs carry iss (who minted) and aud (who may consume). Validating only the signature without checking aud is a common vulnerability — your API must reject tokens minted for a different client.

Multi-tenant mental model

In Keycloak:

  • Realm ≈ tenant
  • Client ≈ your application registration
  • Users live in the realm

Okta/Auth0 use different names (Authorization Server, Application) but the same idea: you register your app and get client_id + credentials.


What you should know after this post

  • You can name all four roles and one example of each
  • You know access vs refresh vs ID token
  • You default to Authorization Code + PKCE for user-facing clients
  • You separate "OAuth tokens" from "my app's session"