Access Control
Guard handlers with session requirements; resolve identity through one provider.
Access control in Base has two halves: decorators state what a handler requires, and a single session context provider — the worker's identity seam — resolves who is calling. The framework enforces the meeting of the two before your handler runs, across HTTP, GraphQL, and RPC alike.
Guard a handler
- Requirements are any-of for both
rolesandentitlements; an empty or absent list requires only authentication. - An anonymous request fails with 401 (
AUTHENTICATION_REQUIRED); an authenticated one missing the roles fails with 403 (PERMISSION_DENIED) — before your handler, before argument deserialization. - The decorator works at class or method level; class-level requirements merge with per-method ones.
- Inside a guarded handler, the session arrives via
@InjectRequestContext(SessionContextRequestKey)— typed and guaranteed present.
@WithSessionAccess is the optional variant: the session loads if present, but anonymous callers pass through — the injected parameter types as SessionContext | undefined. Right for endpoints that personalize when signed in.
The session context
What a provider resolves and your handlers consume:
The provider: One identity seam
A provider implements exactly one method, and a worker has exactly one provider (worker settings or contributed by a module — two is a boot error):
The contract's three-way discipline: a valid session returns a SessionContext; plain "not signed in" returns null; throwing is reserved for policy failures (malformed credentials, suspended account). The provider resolves from the request container, so it can inject services, and it's registered in accessControl, not services.
The Account module in @system-inc/base-modules ships a complete production provider (cookie sessions, roles, organizations) — register the module and the identity seam comes with it.