Files
skills/docs/testkit/testing/testing-integration-oauth-mock/src/endpoints/token.md
2026-05-29 08:20:56 +01:00

2.1 KiB

/token endpoint

Purpose

handleToken implements the OAuth 2.0 token endpoint (RFC-6749 §4.1.3 and §6) for the mock IdP. It supports two grant types: authorization_code (exchanging a freshly issued code for an access+refresh-token pair) and refresh_token (rotating an existing refresh-token).

PKCE verification (RFC-7636) is enforced when the matching auth-code record carries a code_challenge; both plain and S256 challenge methods are supported, and mismatches produce { error: "invalid_grant" } responses with HTTP 400 per spec.

Features

  • authorization_code grant with single-use code semantics.
  • refresh_token grant with rotation — the previous refresh token is revoked on each successful refresh.
  • PKCE S256 verification using a real node:crypto sha256 hash.
  • Configurable expires_in via accessTokenTtlSeconds.
  • RFC-6749 error envelopes: invalid_request, invalid_grant, unsupported_grant_type.

Usage: Basic

const tokenResponse = handleToken(state, {
    grant_type: "authorization_code",
    code: "abc",
    code_verifier: "verifier-43-chars-min",
});

Usage: Advanced

Rotate a refresh token (the original is revoked):

const refreshed = handleToken(state, {
    grant_type: "refresh_token",
    refresh_token: previous.refresh_token,
});

API Reference

  • handleToken(state, form): TokenResult
  • interface TokenSuccess — { status: 200, body: { access_token, token_type: "Bearer", expires_in, refresh_token, scope? } }.
  • interface TokenError — { status: 400|401, body: { error, error_description? } }.

Implementation Notes

PKCE S256 base64url encoding is performed with Buffer.toString('base64') followed by manual = stripping and +// substitution — this matches RFC-4648 §5 and is what every conforming client SDK produces. Auth codes are marked used = true on first successful exchange, and reuse is rejected as invalid_grant. Refresh tokens carry forward the original userPayload so subsequent /userinfo calls keep returning the same identity, modelling production IdP behaviour faithfully.