This chapter walks through building a simple OAuth 2.0 authorization server that implements the authorization code grant. Positioned as the system’s central security authority, the server is responsible for managing client registrations, mediating user consent, and issuing tokens. The text emphasizes the separation of front-channel (browser) and back-channel (server-to-server) interactions, pushing protocol complexity to the authorization server so clients and resource servers can remain simpler.
The implementation begins with static client registration (ID, secret, redirect URIs) and an authorization endpoint that validates inbound requests (known client, matching redirect URI) before presenting a consent page. To protect against attacks, the server uses randomized request IDs and returns the state value unchanged. If the user approves, the server issues a short-lived, one-time authorization code and associates it with the original request for later lookup. The token endpoint then authenticates the client (HTTP Basic or form parameters), validates the grant (correct grant_type, code was issued to this client, and is single use), and returns a JSON response containing a bearer access token, storing token data server-side for later introspection or lookup.
Next, the chapter adds refresh token support so clients can obtain new access tokens without user involvement, ensuring the refresh token belongs to the authenticated client and returning a new access token (often reusing the same refresh token). Scope handling is layered in by letting clients request specific permissions, constraining those requests to each client’s allowed set, and enabling users to approve a subset; the server persists approved scopes with the authorization code and echoes them in the issued tokens. Throughout, the design highlights defensive checks—strict redirect URI validation, careful input handling, one-time code use, and least-privilege scoping—culminating in a minimal but complete authorization server that securely manages client authorization and token lifecycles.
A simple approval page
FAQ
What does the authorization server do, and why is it the most complex OAuth component?
The authorization server authenticates users, registers/manages clients, and issues tokens. During OAuth 2.0’s design, complexity was intentionally concentrated on the authorization server (fewer of them exist compared to clients and resources), so it handles most security-critical logic and validations.How are OAuth clients registered and stored in the chapter’s example?
Clients are statically registered in an in-memory array with fields likeclient_id, client_secret, redirect_uris, and optionally scope. A helper function (for example, getClient) looks up clients by ID. In production, this data would live in a persistent store and often be created via a dynamic registration process.What validations happen at the authorization endpoint before showing the consent page?
- Verify theclient_id identifies a registered client.- Verify the provided
redirect_uri exactly matches one of the client’s registered URIs.- On failures, render a local error page (do not redirect back to the untrusted
redirect_uri).- Persist the original request parameters under a random
reqid to safely resume processing after user consent.How does the user consent flow work in this server?
1) The client sends the user’s browser to/authorize (front channel). 2) The server validates the request and renders a consent page, embedding a random reqid. 3) The form posts to /approve. 4) On denial: redirect the browser to the client’s redirect_uri with error=access_denied. 5) On approval with response_type=code: generate an authorization code, store it with the original request, then redirect with code (and echo the original state if provided).Why must the server echo the client’s state parameter and what CSRF mitigations are used?
The server returns thestate value exactly as received so the client can correlate the response and mitigate CSRF/response-mixup attacks on its side. The server also uses a randomized reqid to link the consent form post back to the original request, adding CSRF protection for the authorization page.How do clients authenticate to the token endpoint, and what errors are used?
Clients authenticate using either HTTP Basic Authorization (preferred) or form parameters—never both at once. The server looks up the client and verifies the secret; failures return401 with { "error": "invalid_client" } (JSON). Token endpoint errors are returned with appropriate HTTP status codes and JSON bodies.How is an authorization code exchanged for an access token?
- Requiregrant_type=authorization_code.- Look up the presented
code and immediately remove it from storage (one-time use).- Verify the code was issued to the authenticated client.
- Generate a random access token, store it (with any associated data like client and scope), and return JSON including
access_token and token_type=Bearer (plus optional fields like expires_in, scope).What’s inside the access token, and how is it stored in this example?
In the example, access tokens are opaque random strings stored in a local NoSQL database. Alternatives include storing hashes of tokens (for safer at-rest handling), using token introspection at the resource, or issuing structured tokens (for example, JWTs) that embed claims and can be validated without a lookup.How are refresh tokens issued and used in this server?
Refresh tokens are issued alongside access tokens and stored separately. To use one, the client calls the token endpoint withgrant_type=refresh_token, the refresh token value, and client authentication. The server finds the token, verifies it was issued to the authenticated client, then issues a new access token and returns it (often reusing the same refresh token). Refresh tokens can expire or be revoked.How is scope handled end-to-end (request, consent, storage, and response)?
- Clients request scopes as a space-delimited string inscope and must not exceed their registered scopes.- The consent page displays requested scopes (checkboxes) so the user can approve a subset.
- The approved scopes are stored with the authorization code; when exchanged, they’re attached to the issued tokens and returned in the token response (space-delimited).
- Refresh requests can optionally down-scope access tokens (issuing fewer rights than originally granted).
OAuth 2 in Action ebook for free