4.1 KiB
url-builder
Purpose
Provides pure URL-building and body-encoding utilities used internally by all OAuth 2.0 grant flows. Keeping these helpers in a single module avoids duplication across the authorization-code, client-credentials, and token-refresh modules while keeping each flow module focused on business logic rather than HTTP serialization details.
These utilities are deliberately stateless and side-effect-free so they can be tested in complete isolation from network calls.
Features
buildAuthorizationUrl— constructs a complete authorization endpoint URL with PKCE, state, scopes, and optional OIDC parameters (nonce,prompt,login_hint).buildTokenRequestBody— serializes a flat string record toapplication/x-www-form-urlencodedusingencodeURIComponent.normalizeTokenResponse— maps a raw token-endpoint JSON body to aTokenSet-compatible shape and derivesexpiresAtfromexpires_in.
Usage: Basic Example
import { buildAuthorizationUrl, buildTokenRequestBody } from "@suaveplan/oauth-client/utils/url-builder";
const url = buildAuthorizationUrl(
{ clientId: "my-app", authorizationUrl: "https://auth.example.com/authorize", tokenUrl: "…" },
{ state: "abc", codeChallenge: "xyz", codeChallengeMethod: "S256" },
);
const body = buildTokenRequestBody({
grant_type: "authorization_code",
code: "AUTH_CODE",
redirect_uri: "https://app.example.com/callback",
client_id: "my-app",
code_verifier: "VERIFIER",
});
Usage: Advanced Example
import {
buildAuthorizationUrl,
normalizeTokenResponse,
} from "@suaveplan/oauth-client/utils/url-builder";
// OIDC authorization request with nonce and prompt
const url = buildAuthorizationUrl(
{
clientId: "my-app",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
redirectUri: "https://app.example.com/callback",
scopes: ["openid", "profile", "email"],
},
{
state: "state123",
codeChallenge: "challenge456",
codeChallengeMethod: "S256",
nonce: "nonce789",
prompt: "consent",
loginHint: "user@example.com",
extra: { access_type: "offline" },
},
);
// Normalize raw token response
const raw = { access_token: "at", token_type: "Bearer", expires_in: 3600 };
const tokenSet = normalizeTokenResponse(raw, Date.now());
// tokenSet.expiresAt is set ~1 hour from now
API Reference
buildAuthorizationUrl(config, params): string
Builds a full authorization URL.
config—OAuthConfigprovidingclientId,authorizationUrl, optionalredirectUriandscopes.params.state— CSRF-protection state value.params.codeChallenge— PKCE code challenge.params.codeChallengeMethod—"S256"or"plain".params.scope?— overridesconfig.scopesfor this request.params.nonce?— OIDC nonce.params.prompt?— UI prompt hint.params.loginHint?— pre-fill login hint.params.extra?— additional query parameters.
buildTokenRequestBody(params: Record<string, string>): string
Returns a percent-encoded application/x-www-form-urlencoded string. Both keys and values are encoded with encodeURIComponent.
normalizeTokenResponse(raw: Record<string, unknown>, now: number): TokenSet
Maps a raw JSON body to { accessToken, tokenType, expiresIn?, expiresAt?, refreshToken?, idToken?, scope? }. The now parameter (milliseconds since epoch) is used to compute expiresAt = now + expiresIn * 1000.
Implementation Notes
buildAuthorizationUrl uses the platform URL constructor and URLSearchParams for correct percent-encoding of complex values. The URL constructor is universally available in all targeted runtimes (Node 10+, browsers, Bun, Deno, edge workers). buildTokenRequestBody uses encodeURIComponent directly rather than URLSearchParams to produce a simple string body that can be passed to fetch without additional wrappers. normalizeTokenResponse is intentionally defensive — it uses typeof guards rather than Zod for performance in hot paths, leaving schema validation to the caller.