/** * P5 authorization SEAM (FIX 6a). These interfaces MIRROR relay-auth's frozen ยง6a shapes * (`UpgradeContext` / `AuthzOutcome` / `DpopContext` / `Authorizer`). P5 (`relay-auth/`) is not * built in this package's build graph, so โ€” per the "program against interfaces / DI seams, * don't hard-import an unbuilt sibling" rule โ€” P1 defines the port here and injects an impl. * * INTEGRATION POINT: when relay-auth lands, replace these local declarations with * `import type { UpgradeContext, AuthzOutcome, DpopContext, Authorizer } from 'relay-auth'` * (structurally identical). P1 holds NO authz logic โ€” the decision is P5's alone. */ import type { CapabilityRight } from 'relay-contracts' /** Proof-of-possession material P5 needs; P1 passes it through, never inspects it. */ export interface DpopContext { readonly proof: string | null readonly publicKeyThumbprint: string | null } /** Everything P5's `onUpgrade`/`onReattach` needs to make the deny-by-default decision. */ export interface UpgradeContext { readonly capabilityRaw: string // opaque token string; P5 verifies the signature readonly originHeader: string // retained for Origin/CSWSH check (P5) readonly expectedAud: string // = the resolved subdomain (Host-confusion guard, INV1) readonly requestedHostId: string // P1 NAMES the resolved host; P5 gates token.host against it readonly requiredRight: CapabilityRight readonly remoteAddrHash: string // salted hash (audit only, INV10) readonly activeSessionCount: number readonly dpop: DpopContext readonly principal: string | null // P5 resolves from the signed token (INV3) } /** P5's verdict. The `ok` branch carries the VERIFIED token's `jti` (OQ5 resolved). */ export type AuthzOutcome = | { readonly ok: true; readonly hostId: string; readonly principal: string; readonly jti: string } | { readonly ok: false; readonly status: 401 | 403 } /** The single authorizer (P5). P1 injects it and supplies the ctx; P5 makes every decision. */ export interface Authorizer { onUpgrade(ctx: UpgradeContext, now: number): Promise onReattach(ctx: UpgradeContext & { readonly sessionId: string }, now: number): Promise }