docs(arch-h1): Phase 13 Sprint 13.4 — OpenAPI auth/sessions + OIDC ops (batch 1, 13 ops)

Phase 13 Sprint 13.4 closure (architecture diligence audit ARCH-H1):
authors OpenAPI operations for the auth/sessions cluster (3) +
auth/oidc CRUD + JWKS + test + refresh cluster (10), drives the
`rest-deferred` exception bucket from 28 → 15.

OpenAPI-only sprint: zero Go changes. Every schema field-by-field
mirrors the projection types in the Phase 9 Sprint 11 sibling-file
handlers (auth_session_oidc_{sessions,crud}.go) + the JWKS-status
surface in auth_users.go + the dry-run discovery result in
internal/auth/oidc/test_discovery.go.

13 new operations
=================

  Sessions cluster (3 ops):
    GET    /api/v1/auth/sessions                listAuthSessions
    DELETE /api/v1/auth/sessions                revokeAuthSessionsExceptCurrent
    DELETE /api/v1/auth/sessions/{id}           revokeAuthSession

  OIDC provider CRUD + JWKS + test + refresh (7 ops):
    GET    /api/v1/auth/oidc/providers                  listOIDCProviders
    POST   /api/v1/auth/oidc/providers                  createOIDCProvider
    PUT    /api/v1/auth/oidc/providers/{id}             updateOIDCProvider
    DELETE /api/v1/auth/oidc/providers/{id}             deleteOIDCProvider
    GET    /api/v1/auth/oidc/providers/{id}/jwks-status getOIDCProviderJWKSStatus
    POST   /api/v1/auth/oidc/providers/{id}/refresh     refreshOIDCProvider
    POST   /api/v1/auth/oidc/test                       testOIDCProvider

  OIDC group-mapping CRUD (3 ops):
    GET    /api/v1/auth/oidc/group-mappings             listOIDCGroupMappings
    POST   /api/v1/auth/oidc/group-mappings             addOIDCGroupMapping
    DELETE /api/v1/auth/oidc/group-mappings/{id}        removeOIDCGroupMapping

8 new schemas (components/schemas)
==================================

  AuthSession                — mirrors sessionResponse (10 fields).
  OIDCProviderResponse       — mirrors oidcProviderResponse (15 fields).
  OIDCProviderRequest        — mirrors oidcProviderRequest (12 fields,
                               client_secret marked password).
  OIDCTestRequest            — mirrors the inline struct in TestProvider
                               (4 fields).
  OIDCTestDiscoveryResult    — mirrors oidc.TestDiscoveryResult
                               (11 fields).
  OIDCJWKSStatusSnapshot     — mirrors oidc.JWKSStatusSnapshot (7
                               fields).
  OIDCGroupMappingResponse   — mirrors groupMappingResponse (6 fields).
  OIDCGroupMappingRequest    — mirrors groupMappingRequest (3 fields,
                               tenant_id deliberately excluded — derived
                               from caller).

Every schema field's JSON tag, type, required-ness, and (where
applicable) description grounded against the Go source byte-for-byte.
Pointer types in Go that the handler marshals via `omitempty` are
modelled as optional fields in the YAML (not present in the
`required` list).

RBAC permissions documented per-operation in the description (matched
against rbacGate wraps in internal/api/router/router.go lines 516-540):
  auth.session.list, auth.session.list.all, auth.session.revoke,
  auth.oidc.list, auth.oidc.create, auth.oidc.edit, auth.oidc.delete.

New tags
========

Added `Sessions` and `OIDC` to the `tags:` list with cross-references
to the handler file paths. Existing operations stay on existing tags;
the new ones declare the new tags.

Exception YAML + baseline
=========================

13 entries removed from api/openapi-handler-exceptions.yaml. The
post-cut shape:

  total entries:           51   (was 64)
  wire-protocol:           36   (unchanged — never burn down)
  rest-deferred:           15   (was 28)

Baseline file bumped 28 → 15. The Sprint 13.1 monotonic-decrease
guard now pins `rest-deferred ≤ 15`. Sprints 13.5 + 13.6 walk it down
to zero (15 → 7 → 0).

YAML header narrative updated to reflect Sprint 13.4 status:
"Sprint 13.4 SHIPPED — 28 - 13 = 15".

Receipts (all from the live tree)
=================================

  $ grep -cE '^\s+operationId:' api/openapi.yaml
    171   (was 158 + 13)

  $ bash scripts/ci-guards/openapi-handler-parity.sh
    Router routes:                  220
    OpenAPI operations:             171
    Documented exceptions:          51
      wire-protocol:                36
      rest-deferred:                15
    openapi-handler-parity: clean.

  $ bash scripts/ci-guards/openapi-rest-deferred-monotonic.sh
    openapi-rest-deferred-monotonic: clean — rest-deferred = 15,
    baseline = 15.

  $ cat api/openapi-handler-exceptions-baseline.txt
    15

  $ python3 -c "import yaml; spec=yaml.safe_load(open('api/openapi.yaml')); ..."
    paths: 126, operations: 171
    components.schemas: 67
    sprint-13.4 schemas missing: (none)
    OpenAPI lint: clean.

  $ gofmt -l .                  → clean
  $ go vet ./internal/api/handler/... ./cmd/server/...  → clean

Sprint 13.5 next (auth/breakglass + auth/users + auth/runtime-config,
8 ops; rest-deferred 15 → 7). Same OpenAPI-only authoring pattern; no
Go changes.

Refs: ARCH-H1 batch 1 closure.
This commit is contained in:
shankar0123
2026-05-14 12:14:13 +00:00
parent a41fc2d75c
commit 952682ebec
3 changed files with 658 additions and 45 deletions
+1 -1
View File
@@ -1 +1 @@
28 15
+7 -44
View File
@@ -41,15 +41,17 @@
# ────────────────────────────────────────────────────────────────────── # ──────────────────────────────────────────────────────────────────────
# #
# Current split, re-derived by the parity script's bucket-reporting # Current split, re-derived by the parity script's bucket-reporting
# subcommand: # subcommand (post-Sprint-13.4 / 2026-05-14):
# #
# total entries: 64 # total entries: 51
# wire-protocol: 36 # wire-protocol: 36
# rest-deferred: 28 # rest-deferred: 15
# #
# Burn-down plan for the rest-deferred bucket (Phase 13 Sprints 13.4-13.6): # Burn-down progress + remaining plan for the rest-deferred bucket:
# #
# Sprint 13.4 28 - 13 = 15 (auth/sessions + auth/oidc cluster, 13 ops) # Sprint 13.4 SHIPPED — 28 - 13 = 15 (auth/sessions cluster 3 ops +
# auth/oidc CRUD + JWKS + test + refresh
# + group-mappings cluster, 10 ops)
# Sprint 13.5 → 15 - 8 = 7 (auth/breakglass + auth/users + # Sprint 13.5 → 15 - 8 = 7 (auth/breakglass + auth/users +
# auth/runtime-config, 8 ops) # auth/runtime-config, 8 ops)
# Sprint 13.6 → 7 - 7 = 0 (audit/export + demo-residual + 3 # Sprint 13.6 → 7 - 7 = 0 (audit/export + demo-residual + 3
@@ -209,45 +211,6 @@ documented_exceptions:
- route: "POST /auth/oidc/back-channel-logout" - route: "POST /auth/oidc/back-channel-logout"
why: "Bundle 2 Phase 5 RFC OIDC Back-Channel Logout 1.0 endpoint. OpenAPI rep deferred to pre-2.2.0." why: "Bundle 2 Phase 5 RFC OIDC Back-Channel Logout 1.0 endpoint. OpenAPI rep deferred to pre-2.2.0."
category: rest-deferred category: rest-deferred
- route: "GET /api/v1/auth/sessions"
why: "Bundle 2 Phase 5 self/admin session list. OpenAPI rep deferred to pre-2.2.0."
category: rest-deferred
- route: "DELETE /api/v1/auth/sessions/{id}"
why: "Bundle 2 Phase 5 session revoke. OpenAPI rep deferred to pre-2.2.0."
category: rest-deferred
- route: "DELETE /api/v1/auth/sessions"
why: "Bundle 2 audit-2026-05-10 MED-2/3 revoke-all-except-current."
category: rest-deferred
- route: "GET /api/v1/auth/oidc/providers"
why: "Bundle 2 Phase 5 OIDC provider CRUD (list)."
category: rest-deferred
- route: "POST /api/v1/auth/oidc/providers"
why: "Bundle 2 Phase 5 OIDC provider CRUD (create)."
category: rest-deferred
- route: "PUT /api/v1/auth/oidc/providers/{id}"
why: "Bundle 2 Phase 5 OIDC provider CRUD (update)."
category: rest-deferred
- route: "DELETE /api/v1/auth/oidc/providers/{id}"
why: "Bundle 2 Phase 5 OIDC provider CRUD (delete)."
category: rest-deferred
- route: "POST /api/v1/auth/oidc/providers/{id}/refresh"
why: "Bundle 2 audit-2026-05-10 MED-7 JWKS hot-refresh."
category: rest-deferred
- route: "GET /api/v1/auth/oidc/providers/{id}/jwks-status"
why: "Bundle 2 audit-2026-05-10 MED-7 JWKS health snapshot."
category: rest-deferred
- route: "POST /api/v1/auth/oidc/test"
why: "Bundle 2 audit-2026-05-10 MED-5 dry-run discovery + JWKS + alg-downgrade check."
category: rest-deferred
- route: "GET /api/v1/auth/oidc/group-mappings"
why: "Bundle 2 Phase 5 group-mapping CRUD (list)."
category: rest-deferred
- route: "POST /api/v1/auth/oidc/group-mappings"
why: "Bundle 2 Phase 5 group-mapping CRUD (create)."
category: rest-deferred
- route: "DELETE /api/v1/auth/oidc/group-mappings/{id}"
why: "Bundle 2 Phase 5 group-mapping CRUD (delete)."
category: rest-deferred
- route: "GET /api/v1/auth/breakglass/credentials" - route: "GET /api/v1/auth/breakglass/credentials"
why: "Bundle 2 Phase 7.5 admin break-glass list (404 when disabled; password hash never on wire)." why: "Bundle 2 Phase 7.5 admin break-glass list (404 when disabled; password hash never on wire)."
category: rest-deferred category: rest-deferred
+650
View File
@@ -75,6 +75,17 @@ tags:
- name: EST - name: EST
description: Enrollment over Secure Transport (RFC 7030) description: Enrollment over Secure Transport (RFC 7030)
- name: SCEP - name: SCEP
- name: Sessions
description: |
Server-side session management. Phase 13 Sprint 13.4 (ARCH-H1
closure batch 1) — authored against the Phase 9 Sprint 11
sibling-file handlers at internal/api/handler/auth_session_oidc_sessions.go.
- name: OIDC
description: |
OIDC identity-provider configuration + group-mapping admin.
Phase 13 Sprint 13.4 — authored against the Phase 9 Sprint 11
sibling-file handlers at internal/api/handler/auth_session_oidc_crud.go +
the JWKS-status surface at internal/api/handler/auth_users.go.
description: Simple Certificate Enrollment Protocol (RFC 8894) description: Simple Certificate Enrollment Protocol (RFC 8894)
paths: paths:
@@ -634,6 +645,385 @@ paths:
"404": { description: Role not assigned to actor } "404": { description: Role not assigned to actor }
"409": { description: Reserved system actor cannot be modified } "409": { description: Reserved system actor cannot be modified }
# ════════════════════════════════════════════════════════════════════
# Phase 13 Sprint 13.4 (ARCH-H1 batch 1) — sessions + OIDC CRUD.
# Authored 2026-05-14 against the Phase 9 Sprint 11 handler
# sibling-files at internal/api/handler/auth_session_oidc_*.go.
# Each schema field-by-field mirrors the projection types
# (sessionResponse / oidcProviderResponse / oidcProviderRequest /
# groupMappingResponse / groupMappingRequest) in
# auth_session_oidc_{sessions,crud}.go.
# ════════════════════════════════════════════════════════════════════
/api/v1/auth/sessions:
get:
tags: [Sessions]
summary: List active sessions (own actor by default; specify actor_id to list another actor's)
description: |
Permission `auth.session.list` for own-sessions. Listing another
actor's sessions additionally requires `auth.session.list.all`
(re-checked inline by the handler; the router-level rbacGate
cannot see the query parameter).
Audit 2026-05-10 MED-2 closure — the all-actors variant is an
admin-class capability, segregated from the same-actor floor.
operationId: listAuthSessions
parameters:
- in: query
name: actor_id
required: false
schema: { type: string }
description: Target actor whose sessions to list. Defaults to the calling actor.
- in: query
name: actor_type
required: false
schema: { type: string }
description: Required when `actor_id` is set and differs from the caller's type. Ignored otherwise.
responses:
"200":
description: Session list
content:
application/json:
schema:
type: object
required: [sessions]
properties:
sessions:
type: array
items:
$ref: "#/components/schemas/AuthSession"
"401": { description: Unauthorized }
"403": { description: Forbidden (auth.session.list missing, or auth.session.list.all missing on cross-actor lookup) }
"500": { description: Internal error }
delete:
tags: [Sessions]
summary: Revoke all sessions for the caller except the current one
description: |
Permission `auth.session.revoke`. Revokes every active session
for the calling actor EXCEPT the session that issued this
request (so the user isn't logged out by the action they just
took). Bearer/API-key callers (whose request has no session
cookie) get all their sessions revoked.
Audit 2026-05-10 MED-3 closure — backs the SessionsPage's
"Sign out all other sessions" button.
Only the `?except=current` form is accepted; any other query
parameter combination returns 400.
operationId: revokeAuthSessionsExceptCurrent
parameters:
- in: query
name: except
required: true
schema: { type: string, enum: [current] }
description: Must be the literal string `current`.
responses:
"200":
description: Revoked count
content:
application/json:
schema:
type: object
required: [revoked_count]
properties:
revoked_count:
type: integer
description: Number of sessions revoked (excludes the current session).
"400": { description: Missing or unsupported query parameters }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"500": { description: Internal error }
/api/v1/auth/sessions/{id}:
delete:
tags: [Sessions]
summary: Revoke a specific session by ID
description: |
Permission `auth.session.revoke`. Revoking your own session is
always allowed (any authenticated caller); revoking another
actor's session requires the same `auth.session.revoke`
permission enforced at the rbacGate.
operationId: revokeAuthSession
parameters:
- in: path
name: id
required: true
schema: { type: string }
description: Session ID.
responses:
"204": { description: Revoked }
"400": { description: Missing session id }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"404": { description: Session not found }
"500": { description: Internal error }
/api/v1/auth/oidc/providers:
get:
tags: [OIDC]
summary: List configured OIDC identity providers
description: Permission `auth.oidc.list`. Returns provider rows for the calling actor's tenant.
operationId: listOIDCProviders
responses:
"200":
description: Provider list
content:
application/json:
schema:
type: object
required: [providers]
properties:
providers:
type: array
items:
$ref: "#/components/schemas/OIDCProviderResponse"
"401": { description: Unauthorized }
"403": { description: Forbidden }
"500": { description: Internal error }
post:
tags: [OIDC]
summary: Create an OIDC identity provider
description: |
Permission `auth.oidc.create`. `client_secret` is required +
encrypted at rest via the config-encryption key
(`CERTCTL_CONFIG_ENCRYPTION_KEY`). The provider is namespaced
by the caller's tenant.
operationId: createOIDCProvider
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCProviderRequest"
responses:
"201":
description: Provider created
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCProviderResponse"
"400": { description: Validation error (missing client_secret, invalid JSON, etc.) }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"409": { description: Provider name already exists for this tenant }
"500": { description: Internal error }
/api/v1/auth/oidc/providers/{id}:
put:
tags: [OIDC]
summary: Update an OIDC identity provider's configuration
description: |
Permission `auth.oidc.edit`. Update is a full replacement: every
OIDCProviderRequest field is honored; `client_secret` is
re-encrypted on every update.
operationId: updateOIDCProvider
parameters:
- in: path
name: id
required: true
schema: { type: string }
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCProviderRequest"
responses:
"200":
description: Provider updated
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCProviderResponse"
"400": { description: Validation error }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"404": { description: Provider not found }
"500": { description: Internal error }
delete:
tags: [OIDC]
summary: Delete an OIDC identity provider
description: |
Permission `auth.oidc.delete`. 409 Conflict is returned when the
provider has active group-mappings or live sessions referencing
it (the operator must remove the dependencies first).
operationId: deleteOIDCProvider
parameters:
- in: path
name: id
required: true
schema: { type: string }
responses:
"204": { description: Provider deleted }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"404": { description: Provider not found }
"409": { description: Provider in use (active group-mappings or sessions reference it) }
"500": { description: Internal error }
/api/v1/auth/oidc/providers/{id}/jwks-status:
get:
tags: [OIDC]
summary: Read per-provider JWKS health (cached keys, refresh count, last error)
description: |
Permission `auth.oidc.list`. Audit 2026-05-10 MED-7 — surfaces
the JWKS verifier state for the named provider so operators can
diagnose IdP key-rotation issues without server logs.
operationId: getOIDCProviderJWKSStatus
parameters:
- in: path
name: id
required: true
schema: { type: string }
responses:
"200":
description: JWKS health snapshot
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCJWKSStatusSnapshot"
"400": { description: Missing provider id }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"404": { description: Provider not found }
"500": { description: Internal error }
/api/v1/auth/oidc/providers/{id}/refresh:
post:
tags: [OIDC]
summary: Force re-fetch of the IdP discovery doc + JWKS for a provider
description: |
Permission `auth.oidc.edit`. Triggers an immediate refetch of
the named provider's OIDC discovery document + JWKS, re-runs
the IdP downgrade-attack defense (Audit 2026-05-10 HIGH-6),
and updates the in-memory verifier cache. Used by the
SessionsPage "Refresh JWKS" button when an operator rotates
IdP keys out-of-band.
operationId: refreshOIDCProvider
parameters:
- in: path
name: id
required: true
schema: { type: string }
responses:
"200":
description: Refresh complete
content:
application/json:
schema:
type: object
required: [refreshed]
properties:
refreshed:
type: boolean
description: Always `true` on success.
"400": { description: Missing provider id, or upstream refresh failed }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"404": { description: Provider not found }
"500": { description: Internal error }
/api/v1/auth/oidc/test:
post:
tags: [OIDC]
summary: Dry-run an OIDC provider config without persisting
description: |
Permission `auth.oidc.create`. Audit 2026-05-10 MED-5 — fetches
the candidate issuer's discovery doc + JWKS, runs the
alg-downgrade defense, parses the RFC 9207 iss-parameter
advert, and returns the per-check report so the GUI can
render a discovery-validation panel before the operator
commits to creating the provider.
operationId: testOIDCProvider
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCTestRequest"
responses:
"200":
description: Discovery + JWKS test report (partial-success cases return 200 with non-empty `errors`)
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCTestDiscoveryResult"
"400": { description: Missing issuer_url, or invalid JSON body }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"500": { description: Internal error (discovery test framework failure) }
/api/v1/auth/oidc/group-mappings:
get:
tags: [OIDC]
summary: List group → role mappings for a provider
description: Permission `auth.oidc.list`. `provider_id` query parameter is required.
operationId: listOIDCGroupMappings
parameters:
- in: query
name: provider_id
required: true
schema: { type: string }
responses:
"200":
description: Group-mapping list
content:
application/json:
schema:
type: object
required: [mappings]
properties:
mappings:
type: array
items:
$ref: "#/components/schemas/OIDCGroupMappingResponse"
"400": { description: Missing provider_id }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"500": { description: Internal error }
post:
tags: [OIDC]
summary: Add a group → role mapping
description: Permission `auth.oidc.edit`. Establishes that members of `group_name` (as advertised by the IdP) receive the named role for the calling tenant.
operationId: addOIDCGroupMapping
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCGroupMappingRequest"
responses:
"201":
description: Mapping created
content:
application/json:
schema:
$ref: "#/components/schemas/OIDCGroupMappingResponse"
"400": { description: Validation error (invalid JSON or missing required fields) }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"409": { description: Mapping already exists (same provider_id + group_name) }
"500": { description: Internal error }
/api/v1/auth/oidc/group-mappings/{id}:
delete:
tags: [OIDC]
summary: Remove a group → role mapping
description: Permission `auth.oidc.edit`.
operationId: removeOIDCGroupMapping
parameters:
- in: path
name: id
required: true
schema: { type: string }
responses:
"204": { description: Mapping removed }
"400": { description: Missing mapping id }
"401": { description: Unauthorized }
"403": { description: Forbidden }
"404": { description: Mapping not found }
"500": { description: Internal error }
/api/v1/version: /api/v1/version:
get: get:
tags: [Health] tags: [Health]
@@ -6411,3 +6801,263 @@ components:
error: error:
type: string type: string
description: Error message when verification failed description: Error message when verification failed
# ════════════════════════════════════════════════════════════════════
# Phase 13 Sprint 13.4 (ARCH-H1 batch 1) schemas. Field-by-field
# mirror of the projection types in
# internal/api/handler/auth_session_oidc_{sessions,crud}.go +
# internal/auth/oidc/test_discovery.go +
# internal/auth/oidc/service.go::JWKSStatusSnapshot.
# ════════════════════════════════════════════════════════════════════
AuthSession:
type: object
description: Mirrors internal/api/handler/auth_session_oidc_sessions.go::sessionResponse.
required: [id, actor_id, actor_type, created_at, last_seen_at, idle_expires_at, absolute_expires_at, revoked]
properties:
id:
type: string
description: Session identifier (UUID-shaped).
actor_id:
type: string
description: Owning actor (user, API key, etc.).
actor_type:
type: string
description: Actor type — `user`, `api_key`, or `actor-demo-anon` in demo mode.
ip_address:
type: string
description: Source IP at session create-time. Omitted when not recorded.
user_agent:
type: string
description: User-Agent header at session create-time. Omitted when not recorded.
created_at:
type: string
format: date-time
description: RFC 3339 UTC timestamp the session was minted.
last_seen_at:
type: string
format: date-time
description: RFC 3339 UTC timestamp the session most-recently validated a request.
idle_expires_at:
type: string
format: date-time
description: RFC 3339 UTC timestamp past which the session is idle-expired (CERTCTL_SESSION_IDLE_TIMEOUT from last_seen_at).
absolute_expires_at:
type: string
format: date-time
description: RFC 3339 UTC timestamp past which the session is absolute-expired regardless of activity (CERTCTL_SESSION_ABSOLUTE_TIMEOUT from created_at).
revoked:
type: boolean
description: True when the session has been revoked (via this API or via back-channel-logout).
OIDCProviderResponse:
type: object
description: Mirrors internal/api/handler/auth_session_oidc_crud.go::oidcProviderResponse.
required:
[id, tenant_id, name, issuer_url, client_id, redirect_uri,
groups_claim_path, groups_claim_format, fetch_userinfo,
iat_window_seconds, jwks_cache_ttl_seconds, created_at, updated_at]
properties:
id:
type: string
description: Provider identifier (`op-` + base64-URL random suffix).
tenant_id:
type: string
description: Owning tenant.
name:
type: string
description: Operator-facing provider name (unique per tenant).
issuer_url:
type: string
description: Canonical OIDC issuer URL. Must match the `iss` claim on returned ID tokens.
client_id:
type: string
description: Client identifier registered with the IdP.
redirect_uri:
type: string
description: Absolute URL the IdP redirects to after authorization.
groups_claim_path:
type: string
description: JSONPath-style claim path that the group→role mapper reads (default `groups`).
groups_claim_format:
type: string
description: How the claim is shaped (default `string_array`).
fetch_userinfo:
type: boolean
description: Whether to call the IdP's userinfo endpoint after token exchange (extends the available claims surface).
scopes:
type: array
items: { type: string }
description: OAuth scopes requested at authorization (typically `openid`, `email`, `profile`, and optionally `groups`).
allowed_email_domains:
type: array
items: { type: string }
description: Whitelisted email-domain suffixes; empty means accept any email-domain.
iat_window_seconds:
type: integer
description: Maximum allowed iat-skew for received ID tokens (default 300).
jwks_cache_ttl_seconds:
type: integer
description: JWKS cache TTL before refetch (default 3600).
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
OIDCProviderRequest:
type: object
description: Mirrors internal/api/handler/auth_session_oidc_crud.go::oidcProviderRequest. `client_secret` is plaintext on the wire only at create/update time; encrypted at rest via `CERTCTL_CONFIG_ENCRYPTION_KEY`.
required: [name, issuer_url, client_id, client_secret, redirect_uri]
properties:
name:
type: string
issuer_url:
type: string
client_id:
type: string
client_secret:
type: string
format: password
description: IdP client secret. Encrypted at rest after submission; never echoed back on read endpoints.
redirect_uri:
type: string
groups_claim_path:
type: string
description: Optional; defaults to `groups` when blank.
groups_claim_format:
type: string
description: Optional; defaults to `string_array` when blank.
fetch_userinfo:
type: boolean
scopes:
type: array
items: { type: string }
allowed_email_domains:
type: array
items: { type: string }
iat_window_seconds:
type: integer
description: Optional; defaults to 300 when zero.
jwks_cache_ttl_seconds:
type: integer
description: Optional; defaults to 3600 when zero.
OIDCTestRequest:
type: object
description: Mirrors the anonymous struct inside auth_session_oidc_crud.go::TestProvider. Discovery-only dry-run; the IdP's discovery + JWKS are fetched and validated WITHOUT persisting anything.
required: [issuer_url]
properties:
issuer_url:
type: string
description: Candidate OIDC issuer URL to dry-run.
client_id:
type: string
description: Optional — only used to confirm the discovery doc advertises matching audience.
client_secret:
type: string
format: password
description: Optional — discovery + JWKS don't require it, but the GUI passes it through so the dry-run shape matches CreateProvider's surface.
scopes:
type: array
items: { type: string }
OIDCTestDiscoveryResult:
type: object
description: Mirrors internal/auth/oidc/test_discovery.go::TestDiscoveryResult. Each field is independently observable so the GUI can render a per-check status row. `errors` is non-empty for both total failures (200 with all checks false) and partial-success cases (200 with some checks true).
required: [discovery_succeeded, jwks_reachable, supported_alg_values, iss_param_supported, current_kids]
properties:
discovery_succeeded:
type: boolean
description: True when `<issuer>/.well-known/openid-configuration` fetched + parsed cleanly.
jwks_reachable:
type: boolean
description: True when the JWKS URI advertised by the discovery doc returned a JWKS document.
supported_alg_values:
type: array
items: { type: string }
description: ID-token signing algorithms the IdP advertises support for.
iss_param_supported:
type: boolean
description: True when the discovery doc advertises support for RFC 9207 `iss` parameter (cross-IdP mix-up defense).
issuer_echo:
type: string
description: The `iss` value the IdP's discovery doc advertises; surfaces IdP misconfigurations where this differs from the issuer URL the operator submitted.
authorization_url:
type: string
token_url:
type: string
jwks_uri:
type: string
userinfo_endpoint:
type: string
current_kids:
type: array
items: { type: string }
description: Current key-IDs reachable in the JWKS document at probe time. Always present in the response payload (empty array when no keys are reachable).
errors:
type: array
items: { type: string }
description: Per-leg failure messages; empty on full success, non-empty on partial-success and full-failure cases.
OIDCJWKSStatusSnapshot:
type: object
description: Mirrors internal/auth/oidc/service.go::JWKSStatusSnapshot. Per-provider JWKS verifier counters surfaced for operator diagnostics.
required: [current_kids, refresh_count, rejected_jws_count, iss_param_supported]
properties:
last_refresh_at:
type: string
format: date-time
description: RFC 3339 UTC timestamp the JWKS was most-recently refreshed. Omitted before the first refresh.
current_kids:
type: array
items: { type: string }
description: Currently-cached JWKS key IDs.
refresh_count:
type: integer
description: Lifetime count of JWKS refresh fetches for this provider.
last_error:
type: string
description: Last refresh-error message; omitted when no refresh has failed.
rejected_jws_count:
type: integer
description: Lifetime count of JWS verifications rejected against this provider's JWKS (debugging hint for IdP key-rotation issues).
iss_param_supported:
type: boolean
description: Whether the provider's discovery doc advertised RFC 9207 `iss` parameter support at the most recent refresh.
OIDCGroupMappingResponse:
type: object
description: Mirrors internal/api/handler/auth_session_oidc_crud.go::groupMappingResponse.
required: [id, provider_id, group_name, role_id, tenant_id, created_at]
properties:
id:
type: string
description: Mapping identifier (`grm-` + base64-URL random suffix).
provider_id:
type: string
description: Owning OIDC provider.
group_name:
type: string
description: Group name as advertised by the IdP's groups claim.
role_id:
type: string
description: Role granted to members of `group_name` for this provider/tenant.
tenant_id:
type: string
created_at:
type: string
format: date-time
OIDCGroupMappingRequest:
type: object
description: Mirrors internal/api/handler/auth_session_oidc_crud.go::groupMappingRequest. Tenant is derived from the calling actor; not accepted from the request body.
required: [provider_id, group_name, role_id]
properties:
provider_id:
type: string
group_name:
type: string
role_id:
type: string