ADR-004: State Validation
Status: Accepted Date: 2025-01-25 Updated: 2026-09-26 (v3: state is always present) Tags: oauth, security, csrf
Problem
- A loopback callback endpoint can be reached by stale browser tabs, other local processes, or any web page linking to it.
- Validating
stateonly when the caller remembered to add it makes CSRF protection opt-in. - Rejecting a mismatched callback only after it resolved still lets unrelated traffic end a legitimate flow.
Decision
- Every flow has a
state. Builder form: the library generates 32 random bytes (base64url) and appends it. URL form: an existing single non-emptystateis used; a missing one is appended; a duplicate or empty one is aTypeErrorbefore anything binds. - A callback completes the flow only if it carries exactly one
stateequal to the expected value, the redirect URI's own query parameters, and eithercode(exactly once, non-empty, noerrorkey) orerror(exactly once, non-empty, nocodekey). Key presence counts:?code=abc&error=is invalid. code,state,error,error_description,error_uriandissmust not repeat, soOAuthCallbackErrorfields are deterministic. Extension parameters stay multi-valued inparams.- Invalid callbacks get a 400 with neutral plain text; the flow keeps waiting under its original deadline.
- A valid
errorcallback ends the flow (OAuthCallbackErrorin core; passed to the SDK'sfinishAuth(), which checksissfirst, in/mcp).
Alternatives (brief)
- Validate only when present (v2) — safe only for callers who add
state; every flow now has one. - Validate after resolving — detects a mismatch but still aborts the real flow.
- Separate
expectedStateoption — duplicates what the URL says and makes validation opt-in.
Impact
- Positive: no callback can complete a flow without its exact
state; invalid traffic can't win the race. - Negative/Risks: URL-form URLs gain a
stateparameter; callers with byte-sensitive (signed) URLs supplystatethemselves or use the builder.
Links
- Code:
src/loopback.ts(isValidCallback),src/get-auth-code.ts - RFC 6749 §10.12; RFC 8252 §7.3, §8.3; RFC 9207
- Related: ADR-007