78. Simplified mint authorization policy
Date: 2026-08-03
Status
Accepted
Supersedes the PER_ORG_FOREIGN_COMPAT mechanism in ADR 0077.
Context
The mint's OIDC token authorization had grown complex: each verifier backend (JWKS, STS) independently implemented the same policy decisions (org-allowed check, per-repo bypass), and the repos scope rules depended on a separate PER_ORG_FOREIGN_COMPAT feature flag that operators had to configure independently of enrollment. The flag existed because ADR 0077 hardened same-org repos scope to requesting-repo-only by default, but org-mode dispatch patterns (.fullsend callers minting across enrolled repos) still needed broader shapes.
This created three pain points:
- Duplicated policy in verifiers. The per-repo bypass and org-allowed checks were copy-pasted into
JWKSVerifier.Verify()andSTSVerifier.prevalidate(), requiring synchronized edits. - Separate knobs for related concerns. Operators had to reason about both
ALLOWED_ORGSmembership andPER_ORG_FOREIGN_COMPATto understand which repos shapes a caller could use. - Public mode fragmentation. Public mode (
ALLOWED_ORGS=*) and per-repo enrollment (PER_REPO_WIF_REPOS) were orthogonal but semantically overlapping.
Decision
Unified authorization policy
Extract the common authorization decision into a single AuthorizeToken function called by both verifiers. The policy is:
repository_ownermust be non-empty (defense-in-depth; GitHub OIDC always populates it, but the check is explicit).- Per-repo mode (
IsPerRepoMode): if the caller'srepositoryclaim appears inPER_REPO_WIF_REPOS(orPER_REPO_WIF_REPOScontains*), the caller is authorized without requiringrepository_ownerinALLOWED_ORGS. Per-repo callers can only mint tokens scoped to their own repository. - Per-org mode: otherwise,
repository_ownermust be inALLOWED_ORGS. Per-org callers get org-mode repos shapes (.fullsendcallers: any non-empty validated list; other enrolled callers:[.fullsend]or{self, .fullsend}).
Public mint mode via PER_REPO_WIF_REPOS
Public mint mode is now expressed as * in PER_REPO_WIF_REPOS rather than * in ALLOWED_ORGS. This means every caller gets per-repo treatment: authorized without org-allow checks, but restricted to requesting-repo-only scope. The ValidateWorkflowRef public-mode path (upstream-only workflow provenance) keys off IsPublicMintRepos(perRepoWIFRepos).
Drop PER_ORG_FOREIGN_COMPAT
The PER_ORG_FOREIGN_COMPAT environment variable, Worker config field, status API field, and CLI display are removed. Org-mode repos shapes are now inherent to per-org callers: if a caller's org is in ALLOWED_ORGS and the caller's repo is not in PER_REPO_WIF_REPOS, the caller automatically gets the broader repos shapes previously gated by the compat flag.
Consequences
- Verifier backends are now pure authenticators: they parse and verify the token, then return claims. The handler calls
AuthorizeToken+ValidateWorkflowRefafter authentication succeeds. Policy changes require editing one function, in one place. - Operators no longer need to set
PER_ORG_FOREIGN_COMPATseparately; per-org functionality follows fromALLOWED_ORGSmembership. - The
GET /v1/statusresponse no longer includesper_org_foreign_compat. Clients parsing this field should ignore its absence. - Existing deployments with
PER_ORG_FOREIGN_COMPAT=trueand orgs inALLOWED_ORGSsee no behavior change: their callers were already both org-enrolled and had compat enabled; now compat is implicit for org-enrolled callers. - Existing deployments with
PER_ORG_FOREIGN_COMPAT=false(or unset) where callers are only inALLOWED_ORGSwill gain org-mode repos shapes they did not previously have. Operators who relied on the flag being off to enforce strict requesting-repo-only scope for org callers should migrate those callers toPER_REPO_WIF_REPOSentries. ValidateWorkflowRefnow keys public mint mode offIsPublicMintRepos(perRepoWIFRepos)instead ofIsPublicMint(allowedOrgs). ExistingALLOWED_ORGS=*deployments withoutPER_REPO_WIF_REPOS=*will see workflow ref validation change — they should addPER_REPO_WIF_REPOS=*to maintain equivalent behavior, or explicitly accept the upstream-only restriction.
Migration
Operators upgrading to this version should review:
Public mint deployments (
ALLOWED_ORGS=*): AddPER_REPO_WIF_REPOS=*to your environment to maintain public mint behavior. Without it,ValidateWorkflowRefwill not enter public-mode (upstream-only) validation, and callers using non-upstream workflow refs will be rejected.PER_ORG_FOREIGN_COMPATremoval: RemovePER_ORG_FOREIGN_COMPATfrom your environment. It is no longer read. Org-mode repos shapes are now implicit forALLOWED_ORGSmembers.Historical per-repo enrollments: Earlier
mint enroll repocommands added orgs to bothPER_REPO_WIF_REPOSandALLOWED_ORGS. The extraALLOWED_ORGSentry is benign (per-repo takes precedence for those callers), but you can clean up stale entries if the org has no other per-org callers.No configuration changes needed for standard per-org deployments (orgs in
ALLOWED_ORGS, no per-repo enrollments, no public mode). These deployments see no behavior change.
