MCP 2025-11-25 · HTTP AUTHORIZATION FIXTURE
MCP OAuth: bind the grant, protect the tool
Compute real S256 proofs and inspect local resource, scope, lifetime and one-use-code checks.
1. Request authorization
Resource: https://mcp.example/mcp · authorization server: https://auth.example
2. Redeem the code
3. Call the protected function
Teaching clock and reset
Computed result
Current state
Action log
Complete evidence
Inspect HTTP authorization without real credentials
This experiment models one protected HTTP MCP endpoint and its separate authorization server. MCP authorization is optional; this lesson does not require OAuth for every server or apply this HTTP flow to standard-input/output transports. The protected-resource metadata identifies the resource and its authorization server. That metadata is fixed here rather than downloaded. The browser computes the actual SHA-256 PKCE challenge, binds a local authorization code, validates its redemption, and executes a protected counter function only after credential and scope checks pass.
Prepare and authorize
PKCE verifier accepts forty-three through one hundred twenty-eight ASCII letters, digits and unreserved punctuation. Generate verifier uses browser cryptographic randomness; the initial example is a published test vector, not a production secret. Requested resource chooses the correct endpoint or an intentionally wrong resource. Requested scope grants exactly read or write, with no implied hierarchy. Consent controls whether a code is issued. Code lifetime sets one through one hundred twenty whole seconds from the current teaching clock. Authorize starts a new local grant, removes the earlier code and token, and records the exact request and computed S256 challenge.
Redeem a bound code
Token lifetime is an independent one-to-one-hundred-twenty-second teaching policy, not an MCP-mandated lifetime. Redemption fault selects a matching exchange, wrong verifier, mismatched callback state, wrong client identifier, wrong redirect URI or different resource. Redeem code checks the current verifier and selected bindings. A callback state mismatch is rejected by the client before the token exchange. Wrong PKCE, client, redirect or expiration produces invalid_grant; a different resource produces invalid_target. An invalid attempt does not consume this fixture’s code, so correcting the input can still succeed before expiry.
Reproduce a successful read and a scope denial
Reset experiment, Authorize, Redeem code, then Call protected tool. At defaults the status is two hundred and the computed counter is zero. Switch Protected operation to write and call again: the status becomes four hundred three with required scope records:write, and the counter stays zero. Select write under Requested scope and authorize and redeem a new grant. The same write now increments the counter to one. Repeated permitted writes increment again; reads return the current value. Write permission does not imply read permission in this deliberately narrow policy.
Separate resource binding from token forwarding
After obtaining a grant, Protected endpoint selects its intended audience or another endpoint. Calling the other endpoint rejects the token with four hundred one before the function executes. Credential selects the issued local handle, an absent token or a foreign-token fixture. The foreign choice represents a credential not issued by the trusted authorization server; it is rejected rather than forwarded through to another service. No editable string is treated as cryptographic proof. Real deployments must validate issuer, audience, expiry and the credential using their actual token format or introspection service.
Test expiry and one-time redemption exactly
Clock advance sets a nonnegative whole number through one hundred twenty; Advance clock adds it immediately. This clock never advances with wall time. At defaults authorize at zero, advance sixty seconds, and redeem: the code is expired at the exact boundary. Reset, authorize and redeem without advancing, then advance sixty and call: the token is expired. Try a fifty-nine-second advance to distinguish the boundary. Redeem a successfully consumed code again: replay is rejected and this fixture revokes the earlier token. That revocation behavior is a stated local policy, not a universal token-server implementation claim.
Understand every action and retained result
Generate verifier replaces only the verifier field, so using it after authorization intentionally breaks the stored challenge binding until a new authorization is started. Changing selectors clears the last action result but preserves the issued state and counter; inspect Current state before continuing. Invalid numeric or verifier inputs show a local editor error and never display an earlier result as a new success. Reset restores all controls, clock zero, counter zero and no credentials. The action log retains at most thirty entries. Expand complete evidence for exact requests, challenges, grant fields and outcomes. These direct cryptographic and counter computations use explicit actions instead of an artificial animation.
Keep the fixture’s limits explicit
The handle is a random local identifier whose trusted fields live in JavaScript memory; it is not a signed JWT, bearer-token implementation or real login. No network, real user, persistence, refresh token, client registration, metadata discovery, TLS validation, MCP initialization or JSON-RPC exchange occurs. The protected function represents the authorization boundary before a tool call. Four hundred one advertises a fixed resource-metadata URL in the no-token example; four hundred three indicates a scope failure. Scope names, lifetime limits, exact resource matching and counter semantics are teaching policy. Never paste production tokens or secrets into this page.
Primary references: MCP HTTP authorization specification and RFC 7636 PKCE and its S256 test vector.