Building an OAuth Client
Building an OAuth 2.0 client means implementing the consumer side of the protocol: registering your app with an authorization server, kicking off the authorization code flow with PKCE, handling the redirect callback, exchanging the code for tokens, and keeping those tokens valid over time via refresh. While most teams reach for a vetted library (Auth.js, oauth4webapi, AppAuth, or a provider SDK) rather than hand-rolling the flow, understanding each step is essential for debugging redirect_uri mismatches, scope errors, and token expiry bugs that libraries can't fully abstract away. This walkthrough builds a minimal but production-realistic client for a web application using the authorization code flow, the safest and most widely supported grant type for apps with a backend.
Cricket analogy: Writing your own OAuth client is like a batter choosing to face raw net bowling from a fast bowling machine instead of relying purely on a coach's video analysis — most professional teams still use analytics software, but understanding the mechanics helps you fix a broken technique the software can't diagnose.
Step 1: Registration and Building the Authorization Request
Before writing any client code, you register the application with the authorization server's developer console, which issues a client_id and lets you pre-register one or more exact redirect_uri values along with the scopes your app is allowed to request. The client then builds the initial authorization request: it generates a cryptographically random code_verifier (43-128 characters), derives a code_challenge as base64url(SHA256(code_verifier)), generates a random state value, persists both verifier and state in the user's session, and redirects the browser to the authorization server's /authorize endpoint with those parameters plus response_type=code, client_id, redirect_uri, and scope.
Cricket analogy: Registering your app is like a domestic team registering its official squad list with the cricket board before the season, locking in exactly who is allowed to play, just as your redirect_uri is locked in before any flow can run.
// Step 1: build the authorization request (Node.js/Express example)
import crypto from 'node:crypto';
function base64url(buf) {
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
app.get('/login', (req, res) => {
const codeVerifier = base64url(crypto.randomBytes(32));
const codeChallenge = base64url(crypto.createHash('sha256').update(codeVerifier).digest());
const state = base64url(crypto.randomBytes(16));
req.session.codeVerifier = codeVerifier;
req.session.state = state;
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.OAUTH_CLIENT_ID,
redirect_uri: 'https://app.example.com/callback',
scope: 'openid profile orders.read',
state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});
res.redirect(`https://auth.example.com/authorize?${params}`);
});Step 2: Handling the Callback and Exchanging the Code
When the user approves the request, the authorization server redirects back to the registered redirect_uri with a code and the same state value the client sent. The callback handler's first job is to compare the returned state against the one stored in the session — mismatched or missing state means the request must be rejected outright, since it likely indicates CSRF. Once state is validated, the client makes a server-to-server POST to the token endpoint with the authorization code, the original code_verifier, redirect_uri, and client_id (plus a client_secret for confidential clients), and receives back an access_token, an expires_in value, typically a refresh_token, and for OpenID Connect flows an id_token.
Cricket analogy: Checking the state parameter is like the third umpire checking the exact timestamp on a run-out replay matches the ball being reviewed, rejecting the review outright if the footage doesn't line up with the claimed delivery.
Always validate the state parameter server-side before doing anything else with the callback. Skipping this check, or comparing it loosely, reopens the CSRF hole PKCE and state together are designed to close, letting an attacker trick a victim into linking the attacker's account.
- Register your app first: client_id, exact redirect_uri, and allowed scopes are set up in the authorization server's console.
- Generate a random code_verifier and derive code_challenge via SHA-256 before redirecting the user to /authorize.
- Always generate a fresh, random state value per request and persist it server-side for later comparison.
- Validate state on the callback before doing anything else; a mismatch means reject the request.
- Exchange the code for tokens via a server-to-server POST including the code_verifier, never in the browser.
- Store the returned access_token, refresh_token, and expires_in securely, and set up refresh before expiry.
- Prefer a vetted OAuth client library over hand-rolling the flow in production.
Practice what you learned
1. What must a client do before it can start the authorization code flow?
2. What should happen if the state parameter returned in a callback doesn't match the one the client stored?
3. Where should the code-for-token exchange request be made from?
4. What does the token endpoint require in addition to the authorization code for a PKCE flow?
Was this page helpful?
You May Also Like
OAuth Best Practices
The concrete, RFC 9700-aligned security practices every OAuth 2.0 implementation should follow, from PKCE to token storage.
OAuth Quick Reference
A condensed lookup of OAuth 2.0 grant types, standard endpoints, token parameters, and common error codes for day-to-day implementation work.
OAuth 2.0 vs OAuth 1.0
A technical comparison of OAuth 1.0's signed-request model against OAuth 2.0's bearer-token, multi-grant-type design.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics