---
id: WF-PFM-SEC-009
title: Authorize a request at the gateway
trigger: A protected human API request reaches the gateway authorizer for fresh evaluation or cached decision
  use.
product_area: platform
module: security
revision: 1
status: draft
design_intent: confirmed
origin: services-platform:docs/proposals/security-identity-and-access.md (identity v3 §7.1, the order of checks);
  docs/proposals/security-authorization.md (authorization revision 2 §10.2, caching and the signed context)
implementation_evidence: observed
inference: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
implements:
- CAP-PFM-SEC-015
- CAP-PFM-SEC-007
- CAP-PFM-SEC-002
crosses:
- SVC-PFM-SEC-security
- SVC-PFM-SEC-tenants
idempotency: Fresh authorization does not create membership or identity records. Gateway cache keys include
  token, method and route template. One decision log is emitted per authorizer invocation, so cached gateway
  requests do not necessarily invoke or log fresh evaluation.
concurrency: 'Fresh decisions use current database reads alongside per-process pool/manifest caches: sixty
  seconds fresh, with a last-known-good value for up to fifteen minutes during source failure. Missing usable
  metadata fails closed.'
hops:
- n: 1
  actor_or_service: Authorizer (front door)
  action: Identify the token issuer without trusting its claims and locate a registered active pool.
  anchor: services-platform:modules/security/authorizer/front-door/front-door.ts
  seam: sync
  on_failure: Reject unknown issuers and inactive pools with unauthorized status.
  left_inconsistent: No membership or identity is created.
  observer: The gateway response and authorization decision log expose rejection.
  provenance: observed
  source: services-platform:modules/security/authorizer/authorize.ts:authorize (steps 1 and 2)
- n: 2
  actor_or_service: Authorizer (verify and match)
  action: Verify token signature, issuer, access use, client and time, then resolve active and staged route
    requirements.
  anchor: services-platform:modules/security/authorizer/registry/route-match.ts
  seam: sync
  on_failure: Reject invalid tokens and deny undeclared routes.
  left_inconsistent: No authorization writes occur.
  observer: The gateway response and decision log show token or registry refusal.
  provenance: observed
  source: services-platform:modules/security/authorizer/authorize.ts:authorize (steps 3 to 5)
- n: 3
  actor_or_service: Authorizer (walls)
  action: Check token wall and target boundary, resolve the existing identity and require active membership
    and reachable workspace.
  anchor: services-platform:modules/security/authorizer/authorize.ts:authorize
  seam: sync
  on_failure: Deny wall, identity, status or workspace-boundary mismatch.
  left_inconsistent: No new identity or membership is provisioned.
  observer: The decision log identifies the refused boundary category.
  provenance: observed
  source: services-platform:modules/security/authorizer/authorize.ts:authorize (steps 6 to 10)
- n: 4
  actor_or_service: Authorizer (grant slice)
  action: Resolve scoped assignments and HQ platform authority, and compute reachable workspaces. License state
    constrains possible grants only in enforce mode; an absent licensing configuration defaults to off.
  anchor: services-platform:modules/security/services/persistence/assignments.ts:resolveGrantSlice
  seam: sync
  on_failure: Deny missing reach or staff authority; refuse and alarm oversized slices.
  left_inconsistent: No grant is created by evaluation.
  observer: Decision logs and slice-size alarms expose the refusal.
  provenance: observed
  source: services-platform:modules/security/authorizer/authorize.ts:authorize (steps 11 to 13); modules/security/authorizer/grants/possible-grant.ts
    (licensing mode); modules/security/authorizer/authorize.ts:AuthorizerDependencies
- n: 5
  actor_or_service: Authorizer (policy and context)
  action: Return an enumerated allow policy and signed context with membership, contact link, grant slice,
    releases and a ninety-second validity interval.
  anchor: services-platform:modules/security/authorizer/context/signed-context.ts
  seam: sync
  on_failure: Refuse the request if signing fails.
  left_inconsistent: No service request proceeds under an unsigned context.
  observer: Gateway errors and signing logs expose failure.
  provenance: observed
  source: services-platform:modules/security/authorizer/context/signed-context.ts (header)
- n: 6
  actor_or_service: The receiving service's dispatcher
  action: At the service, verify context and evaluate route permissions with request conditions, ownership
    and workspace availability.
  anchor: services-platform:modules/tenants/services/access/tenant-availability.ts:tenantAvailability
  seam: sync
  on_failure: Return forbidden for refused permission or availability; deleting workspaces permit only eligible
    staff reads.
  left_inconsistent: No refused operation is executed.
  observer: The service response identifies its refusal.
  provenance: observed
  source: services-platform:modules/tenants/services/access/tenant-availability.ts (header)
branches:
- Machine calls use cloud-role and registered-principal authorization through their separate path.
- Public routes bypass the human authorizer; unexpected authorizer invocation is refused.
waits:
- Gateway allow/deny decisions are cached for sixty seconds; their signed contexts expire thirty seconds beyond
  that cache bound.
evidence:
- 'services-platform at ad3f6a65: modules/security/authorizer/*; aws/lambda/AUTHORIZER.handler.ts; services/persistence/assignments.ts;
  modules/tenants/services/access/tenant-availability.ts; docs/security/grant-slice-growth.md.'
- services-platform:modules/security/authorizer/authorize.ts (dependencies.config.licensing defaults to off)
examined_at: '2026-10-23'
examiner: codex
controls: [TC-AVL-001, TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CHG-005, TC-CHG-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-003, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-003, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-PRV-004, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Authorize a request at the gateway

## What It Does

The gateway and receiving service share responsibility: the gateway verifies identity and builds the bounded grant context, while the service evaluates its declared operation, ownership and availability.

## The Flow

```mermaid
flowchart TD
    H1["Identify active pool"]
    H2["Verify token and declared route"]
    H3["Check membership boundary"]
    H4["Resolve scoped authority"]
    H5["Sign bounded context"]
    H6["Evaluate service operation"]
    H1 --> H2
    H2 --> H3
    H3 --> H4
    H4 --> H5
    H5 --> H6
```

The diagram separates the relevant success, alternative and failure paths. The numbered hop rows retain the detailed authority checks, delegated work and retry limits.

## When Something Fails

The workflow applies to protected human routes. Machine and public routes have separate access contracts. No authorization step creates a missing membership, and a cached decision does not prove that the current database would still allow the request.

Fresh authorization does not create membership or identity records. Gateway cache keys include token, method and route template. One decision log is emitted per authorizer invocation, so cached gateway requests do not necessarily invoke or log fresh evaluation.

Fresh decisions use current database reads alongside per-process pool/manifest caches: sixty seconds fresh, with a last-known-good value for up to fifteen minutes during source failure. Missing usable metadata fails closed.

## Open Design Questions

The recorded findings retain any stronger planning expectation, missing writer or unproved recovery path. Observed behavior does not approve those outstanding product choices.


License enforcement depends on the deployed mode. Unconfigured authorization defaults to licensing off; the design does not infer enforcement from the presence of a license record alone.
