testing/cross_backend/token_scope_surface.ts

Cross-backend parity suite for token scoping's non-RPC surface rule — *a narrowed api token is RPC-only*.

Why this suite exists

Every existing cross-impl token-scope assertion is the rpc:<method> arm (the token_scope_cases batch in the conformance security slate). The surface:<name> arm had none — and it is the load-bearing half of the design. Both spines implement it, at the same named surfaces, returning a denial body that is an explicit cross-impl contract (token_scope_denied_bodyfuz_auth::token_scope_surface_denied_response), and nothing compared the two.

That gap has the shape this control family keeps finding: the per-method arm was the one everybody looked at, so it was the one that got a gate. The surface arm is where a divergence could sit indefinitely — the schema gate can't see it (no column), the action-manifest gate can't see it (no method), and the spec-derived suites can't see it (these are REST routes, not the declared RPC surface).

What it pins

  • The denial body, byte for byte — status, error, and the exact required_scope capability string, per surface. A spine that renamed a surface, dropped the surface: prefix, or emitted the JSON-RPC envelope from a REST route fails here.
  • Scope outranks role — a narrowed token whose account *also* lacks the gating role must hear token_scope_required, not insufficient_permissions. This is the observable the TS↔Rust ordering divergence produced before both spines converged on running the surface gate ahead of the role gate; it was explicitly *invisible* to the cross-backend suites, because every scoped-token probe used a token whose account happened to hold the role.

Two non-vacuity controls hold the negatives honest: the narrowed token must still reach the RPC method it *does* name (otherwise a spine that rejected the credential outright would pass every denial), and a full bearer must reach the same surface (otherwise a route broken for all bearers would pass too).

Why the WS upgrade isn't here

Rule 3's third probeable surface is the WebSocket upgrade, and it is deliberately omitted: create_ws_transport threads cookies, not bearer headers, and a refused upgrade surfaces as a thrown connection error whose body — the thing this suite exists to compare — isn't readable. The two HTTP surfaces are where the denial body is observable, which is what the parity gate needs. The WS gate keeps its per-spine coverage (fuz_app's surface census pins the call site; the Rust census pins its own).

$lib-free by contract (relative specifiers only), like the sibling cross-backend suites.

view source

Declarations
#

2 declarations

describe_token_scope_surface_cross_tests
#

TokenScopeSurfaceCrossTestOptions
#

Depends on
#