Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/proxy-token-lifetimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ Issuance through createPostgresTokenManagementService uses the persisted target

The service resolves the actor's current IAM snapshot, evaluates iam:Manage for the target, and persists the required authorization decision before reading the target kind. Denied policy and audit failure stop before the kind read. The owner read must return exactly one matching ID with a recognized kind; missing, malformed, duplicate, or failed reads prevent issuance.

After reading the cap, capture one timestamp for both the lifetime check and credential creation. Invalid expiry, unavailable kind, and failed issuance use the existing safe TokenManagementUnavailable contract. An allowed decision is authorization metadata, not an issuance-success claim. Credential mutation and the issued lifecycle event occur only on successful issuance and remain atomic.
After reading the cap, capture one timestamp for both the lifetime check and credential creation. Authorized past/equal-now or over-cap expiry throws fixed InvalidTokenManagementLifetime / Invalid token management lifetime. Invalid cap/clock, unavailable kind and failed issuance retain the safe TokenManagementUnavailable contract. The coordinator reconstructs only this typed lifetime error; spoofed error names and unexpected issuance failures remain unavailable. An allowed decision is authorization metadata, not an issuance-success claim. Credential mutation and the issued lifecycle event occur only on successful issuance and remain atomic.

Revocation and previously issued credentials retain their existing behavior; no retroactive truncation occurs. The lower-level trusted coordinator and credential primitive remain infrastructure boundaries with optional caller-supplied issuance guards; the product service always supplies the fixed kind-based guard. SSO authentication and a public management API are not introduced here. No transaction spans actor snapshot, authorization audit, target-kind read, and credential write.
6 changes: 3 additions & 3 deletions contracts/sso-management-foundation.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# SSO management foundation: planning status

The internal RFC 9068 JWT verifier and token-management composition are delivered under #488; public management HTTP authentication remains pending. The binding read foundation is delivered under #486, and lifetime caps are enforced at the existing internal service under the [lifetime contract](proxy-token-lifetimes.md). This is not an implemented public HTTP contract:
The internal RFC 9068 JWT verifier and token-management composition are delivered under #488; the confirmed separate [public management HTTP contract](sso-management-http.md) is implemented under #492. The binding read foundation is delivered under #486, and lifetime caps are enforced at the existing internal service under the [lifetime contract](proxy-token-lifetimes.md). The agreed foundation is:

- Company SSO uses OIDC.
- The first management API authenticates SSO human users only. Service accounts continue using the LLM proxy; service management credentials require a later decision.
- Newly issued proxy tokens have a mandatory expiry and maximum lifetime of 30 days for human owners or 90 days for service owners.
- Existing iam:Manage policy evaluation on the target principal and required metadata decision audit remain in force. SSO authentication supplies identity, not an authorization bypass.

Confirmed on 2026-10-11: administrator-pre-registered issuer/subject bindings, API-specific SSO JWT access tokens and the RFC 9068 access-token profile; no automatic first-login creation. The [binding read foundation](sso-identity-bindings.md) is implemented under #486 and [internal token-management composition](sso-token-management.md) under #488. The contributor also confirmed registered RS256 public keys, maximum one-hour lifetime/age with 60-second clock tolerance, and required auth_time. Administrators must trust its end-user semantics. HTTP paths/errors/audit and credential migration handling remain separate. Recommendations are not requirements until confirmed.
Confirmed on 2026-10-11: administrator-pre-registered issuer/subject bindings, API-specific SSO JWT access tokens and the RFC 9068 access-token profile; no automatic first-login creation. The [binding read foundation](sso-identity-bindings.md) is implemented under #486 and [internal token-management composition](sso-token-management.md) under #488. The contributor also confirmed registered RS256 public keys, maximum one-hour lifetime/age with 60-second clock tolerance, and required auth_time. Administrators must trust its end-user semantics. The contributor confirmed POST issue/revoke paths, no-store fixed errors, anonymous management authentication audit and 400 for authorized invalid lifetime under #492. Credential migration handling and deployment activation remain separate.

The current internal facade, opaque credential primitives, and proxy HTTP endpoints remain as documented in [token management](token-management.md). The public management HTTP API remains pending; the binding read foundation and internal lifetime enforcement are delivered separately.
The current internal facade, opaque credential primitives, and proxy HTTP endpoints remain as documented in [token management](token-management.md). The public management HTTP factories are available separately; owned runtime activation and audited binding provisioning remain follow-up work.
46 changes: 46 additions & 0 deletions contracts/sso-management-http.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Public SSO proxy-token management HTTP API

Plan: [492-sso-management-http](../docs/plans/492-sso-management-http.md).

## Factories and identity

createSsoManagementHandler({newRequestId, authenticate, issue, revoke, writeAudit}) provides the injected Fetch boundary; these ports are trusted deployment code, never request fields. createSsoManagementServer provides the same boundary as an unbound Node HTTP server. createPostgresRfc9068SsoManagementHandler / createPostgresRfc9068SsoManagementServer accept {client, newRequestId, now, issuer, audience, jwks}, capture configuration once and compose the [local-key RFC 9068 verifier](sso-access-tokens.md), [current active-human binding](sso-human-authentication.md), [current IAM management service](token-management.md) and required gateway audit store. Invalid runtime configuration throws only the fixed InvalidSsoAccessTokenConfiguration error.

The caller owns listening, migration, deployment routing, TLS and database lifetime. Existing owned proxy runtimes do not activate these routes. Configuration/key changes require rebuilding the factory. Signed auth_time requires the registered issuer to attest actual human login. Service principals, unregistered identities and inactive humans cannot authenticate. No caller actor ID, claims object or proxy credential substitutes for SSO authentication; authentication is verified once for every request, then current IAM is loaded after body validation. There is no authentication/policy cache or administrator bypass.

## Routes and request boundaries

| Route | Exact JSON object | Success |
| --- | --- | --- |
| POST /management/v1/proxy-tokens | {principalId: string, expiresAt: number} | 201 {credentialId: string, token: string} |
| POST /management/v1/proxy-tokens/revoke | {credentialId: string} | 200 {revoked: boolean} |

Authentication uses one case-insensitive Bearer scheme and one nonblank token without whitespace or commas. Authenticate before consuming the body, checking its media type/shape or query parameters. Malformed/missing Bearer, rejected JWT and missing/current non-human binding are 401. Unexpected authentication failures are 503. Authentication failures require anonymous audit before responding.

After successful authentication, require application/json (case-insensitive media type, parameters permitted), valid fatal-decoded UTF-8 JSON and at most 16384 streamed bytes. Do not trust Content-Length to enforce the bound. Accept exactly the operation's own keys, rejecting extra keys including actor IDs, policy/cap overrides and request IDs. IDs are nonempty well-formed Unicode, at most 256 UTF-16 units and without NUL. expiresAt is an absolute epoch-millisecond nonnegative safe integer. Reject query parameters, malformed/empty bodies, invalid encoding, oversize streams and body-read failures with 400 before current IAM/mutation. Bound/cancel consumed streams and release the reader. Pre-dispatch cancellation stops mutation; cancellation after service dispatch does not certify rollback.

newRequestId supplies a trusted server-generated ID of 1..256 ASCII characters, matching [A-Za-z0-9][A-Za-z0-9._+:/@-]*. Never use caller X-Request-Id as authority or idempotency. Recognized requests return X-Request-Id; errors also include request_id. Generator failure/invalid IDs return safe 503 without reflection. Unknown paths or methods use fixed 404 before authentication/IDs. Node adapter failures before handler invocation use safe 503/no-store; the generic proxy bridge's default 500 response remains unchanged.

## Permission, expiry and response semantics

Only the derived active-human authenticatedActorId enters the trusted actor service. Reuse persisted current direct/role IAM attachments, iam:Manage on principal:<owner>, explicit Deny precedence, mandatory Allow/Deny decision audit and atomic credential lifecycle events. Unknown credentials deny without revealing an owner (403). A known already-revoked credential still requires owner-scoped current IAM and required decision audit before 200 {revoked:false}; no second lifecycle event is created.

For authorized issuance, persist the Allow decision before owner-kind lookup and lifetime checks. Human owners permit 30 days and service owners 90 days, from the single captured issuance timestamp. Past/equal-now or over-cap expiry throws fixed InvalidTokenManagementLifetime / Invalid token management lifetime and maps to 400 invalid_request. Owner/cap/clock/database/mutation failures remain unavailable (503). Policy Deny remains 403, and required decision/lifecycle audit failures remain 503 even when expiry is invalid. An Allow decision is not a success claim. Lower-level issuance without a lifetime guard retains its previous primitive error mapping.

All success, validation, authentication, authorization, availability, routing and Node fallback responses include Cache-Control: no-store. Success issuance projects only credentialId and the one-time token; revocation projects only revoked. Malformed trusted service outputs cannot expose partial secret material or claim completion. Fixed OpenGranter error envelope: {error:{code,message},request_id}; request_id is omitted when no valid server ID exists. No underlying error message, cause, request body or SSO claim is serialized.

| Status | Fixed code | Meaning |
| --- | --- | --- |
| 400 | invalid_request | Body/query validation, invalid authorized lifetime or pre-dispatch cancellation |
| 401 | unauthorized | Missing/rejected SSO identity; WWW-Authenticate: Bearer |
| 403 | forbidden | Current owner-scoped IAM denial |
| 503 | authentication_unavailable | Verification/binding dependency failure after successful auth-failure audit |
| 503 | audit_unavailable | Required anonymous authentication audit failed |
| 503 | internal_error | Request ID, current IAM/credential/decision audit or Node adapter unavailable |
| 404 | not_found | Unknown path or unsupported method |

## Anonymous authentication audit and limits

Use gateway_audit_events without a new migration. management-auth-denied and management-auth-unavailable project server timestamp, request ID and details:{operation:'issue'|'revoke'}. principal_id, credential_id and policy_versions are null even if the caller supplies extra fields. Never persist JWTs, issuer, subject, body, proxy secret or dependency messages. Failed append suppresses the original auth response and returns 503 audit_unavailable. Successful authentication creates no anonymous event; existing IAM decisions and atomic credential lifecycle events record authorized/denied work. Malformed authenticated bodies create no fabricated IAM decision. Existing principal audit-history visibility remains unchanged.

The boundary performs no inference, provider secret lookup, limit check or usage accounting. It preserves all proxy IAM/limits/audit/usage paths by not changing them. Separate binding, policy, cap and credential reads are not one transaction and do not revalidate intervening changes. There is no automatic retry, replay detection, introspection, access-token revocation check or issuance deduplication. A disconnect or response failure after issuance can leave a valid credential whose secret the caller did not receive; request IDs are correlation only. Audited binding provisioning, owned runtime activation, network key refresh and complete release gates remain follow-up work.
2 changes: 1 addition & 1 deletion contracts/sso-token-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ Verifier rejection and missing/inactive/service/unbound identity throw `SsoManag

After authentication, the [existing management service](token-management.md) reloads current actor policies and enforces `iam:Manage` on the stored credential owner or requested issuance principal. Implicit/explicit Deny, required authorization decision audit, owner-kind lifetime caps and atomic lifecycle writes remain unchanged. Authentication supplies no permissions, extra audit kinds, secrets, inference or usage calls. Neither access tokens nor issuer/subject claims are included in management decisions/lifecycle events; only existing nonsecret actor/target/request/credential/policy identifiers appear. Issued raw proxy tokens return once under the existing contract.

Each operation re-verifies its access token and reloads binding/kind/active and policy state; no authentication cache. Request mutation during verification/SQL cannot change the captured operation. Changes between calls take effect. Binding and policy reads are separate and state changes after a read are not transactionally revalidated. Audited administrator provisioning, public API paths/errors/audit, automatic JWKS rotation/introspection/replay controls and full release gates remain pending.
Each operation re-verifies its access token and reloads binding/kind/active and policy state; no authentication cache. Request mutation during verification/SQL cannot change the captured operation. Changes between calls take effect. Binding and policy reads are separate and state changes after a read are not transactionally revalidated. The separate [public HTTP contract](sso-management-http.md) now selects paths/errors/anonymous authentication audit and distinguishes authorized invalid lifetime errors. Audited administrator provisioning, owned runtime activation, automatic JWKS rotation/introspection/replay controls and full release gates remain pending.

The reusable [SSO human authenticator](sso-human-authentication.md) now owns the verification/binding stage. Existing operation ordering and safe error constructors are preserved, including gateway-module re-exports; this refactor adds no public HTTP behavior.
4 changes: 2 additions & 2 deletions contracts/token-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
- Each operation reads one consistent current actor/role/policy snapshot, with no actor cache. Extra caller fields (including actor state, statements, policy versions, or nominated owner) cannot affect evaluation or audit.
- Missing/inactive actors are denied with empty evaluated policy versions. Active unattached actors default deny; direct and inherited statements apply together, with explicit Deny precedence.
- Successful snapshot resolution feeds the existing `iam:Manage` owner-scoped coordinator. Required allow/deny decision persistence precedes credential mutation. Credential lifecycle events record successful completion atomically with mutation.
- Invalid inputs throw `InvalidTokenManagementInput` before SQL. Valid policy denials throw `TokenManagementDenied` after required decision persistence. Snapshot/read/write failures throw fixed `TokenManagementUnavailable` with no driver cause or secrets and no credential mutation.
- Invalid inputs throw `InvalidTokenManagementInput` before SQL. Valid policy denials throw `TokenManagementDenied` after required decision persistence. After Allow and required audit, a guarded issuance can throw fixed InvalidTokenManagementLifetime for past/equal-now or over-cap expiry; it contains no underlying cause or detail. Snapshot/read/write failures throw fixed `TokenManagementUnavailable` with no driver cause or secrets and no credential mutation.
- A failed actor snapshot read cannot produce a reliable policy decision and therefore writes no allowed/denied decision. A decision-write failure also prevents target-kind lookup and mutation. Missing/malformed/unavailable target kind and invalid post-authorization lifetime fail safely without credential/lifecycle writes; an allowed decision is not an issuance-success event.
- Policies changed between operations affect the next read. Changes concurrent with an operation after its snapshot read are not revalidated; no transaction spans snapshot, decision, and mutation.

## Executable cases

`test/postgres-token-management-service.test.ts` covers persisted role grants, direct Deny precedence, next-operation policy/attachment/active-state changes, missing actors, ignored forged fields, immutable owner scoping, malformed/unavailable snapshots, required decision-store failure, and invalid inputs before SQL.

The [internal SSO composition](sso-token-management.md) derives this trusted actor ID from an injected verified access-token identity and a current pre-registered human binding. It retains these authorization/mutation controls and has a configured RFC 9068 verifier; public HTTP authentication remains pending.
The [internal SSO composition](sso-token-management.md) derives this trusted actor ID from an injected verified access-token identity and a current pre-registered human binding. It retains these authorization/mutation controls and has a configured RFC 9068 verifier; the separate [public HTTP boundary](sso-management-http.md) authenticates before body parsing and reuses this service.

## Operation snapshots

Expand Down
Loading
Loading