You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(spec,core,rest,runtime): declare ExecutionContext.authGate so the ADR-0069 gate joins the closed field set (#7280) (#7434)
The authentication-policy gate rode the execution context undeclared: REST's
`computeExecCtx` spread it on with `...(authGate ? { authGate } : {})` behind an
`as any`, and `enforceAuth` read it back ten lines later. #6216's closed entry
field set is derived from `keyof ExecutionContext`, so a field living only
inside an `as any` is outside every closure gate by construction — the exact
blind spot that gate exists to remove.
Measured as ENTRY-decided, not a mid-request mutation: it is resolved from the
request's own session inside `computeExecCtx`, immediately before assembly, and
no handler writes it. So it joins the closed set rather than the non-entry
partition, and `ExecutionContextAssemblyInput` gains a REQUIRED `authGate` input
on the `accessToken` template — every face decides on the record. REST carries
it (its consumer reads it off the envelope); the runtime/MCP dispatcher passes
`undefined` because it enforces the same gate at its own seam
(`HttpDispatcher.enforceAuthGate`) and never reads `context.authGate`.
`normalizeAuthGate` completes a session user's loose gate into the declared
shape at the one producer, so a gate naming a code but no message no longer
renders a 403 body with `message: undefined`. `AuthGate` is now derived from the
schema instead of being a second hand-written declaration.
No runtime behaviour change: the assembler omits undefined-valued keys, so the
key is present exactly when it was before.
Claude-Session: https://claude.ai/code/session_01PiRUoQkTSBBmpyXBY3cVn2
Co-authored-by: Claude <noreply@anthropic.com>
|**posture**|`Enum<'PLATFORM_ADMIN' \| 'TENANT_ADMIN' \| 'MEMBER' \| 'EXTERNAL'>`| optional | ADR-0095 D2 posture rung — PLATFORM_ADMIN crosses the tenant wall where object posture permits; TENANT_ADMIN sees all rows in the org; MEMBER gets business RLS; EXTERNAL sees only explicitly shared rows. |
56
+
|**authGate**|`{ code: string; message: string }`| optional | ADR-0069 authentication-policy gate: present only while the principal is blocked from protected resources until they remediate (expired password, enforced MFA), absent for every healthy session. `code` is the stable machine code the client branches on (PASSWORD_EXPIRED / MFA_REQUIRED) and `message` is what the blocked user reads; both are required because the transport seam renders them as the 403 body. AUTHENTICATION, not authorization — it suspends access entirely rather than narrowing it, and nothing in the permission/RLS path reads it, while the allow-listed remediation endpoints stay reachable. Server-constructed only, never client-supplied; a guest/anonymous principal never carries one. |
0 commit comments