first commit
This commit is contained in:
94
docs/identity/auth/oauth-client/src/utils/url-builder.md
Normal file
94
docs/identity/auth/oauth-client/src/utils/url-builder.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# 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 to `application/x-www-form-urlencoded` using `encodeURIComponent`.
|
||||
- `normalizeTokenResponse` — maps a raw token-endpoint JSON body to a `TokenSet`-compatible shape and derives `expiresAt` from `expires_in`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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` — `OAuthConfig` providing `clientId`, `authorizationUrl`, optional `redirectUri` and `scopes`.
|
||||
- `params.state` — CSRF-protection state value.
|
||||
- `params.codeChallenge` — PKCE code challenge.
|
||||
- `params.codeChallengeMethod` — `"S256"` or `"plain"`.
|
||||
- `params.scope?` — overrides `config.scopes` for 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.
|
||||
Reference in New Issue
Block a user