Skip to content

Control-Plane Authentication

This document describes the authentication and authorization options currently implemented in control-plane/service as of July 17, 2026.

The control-plane supports these runtime auth modes:

  1. auth-disabled (no authentication; development/testing mode).
  2. OIDC browser session auth (currently Google OIDC via login/callback).
  3. OIDC bearer auth for tools/CLI (ID token in Authorization: Bearer ...).
  4. Auth Service bearer auth for Governance Studio integrations (delegated token validation).
  5. Hybrid mode: browser session auth + either bearer verification mode enabled together.

Set:

  • -auth-enabled=false

Behavior:

  • Requests are treated as an admin principal internally.
  • No cookie session or bearer token is required.
  • Intended for local/dev scenarios only.

Important caveat:

  • POST /v1/agent-registration-tokens requires an authenticated subject (sub) to mint registration tokens. In auth-disabled mode, no subject exists, so this endpoint returns unauthorized.

Set:

  • -auth-enabled=true
  • -auth-google-enabled=true
  • -auth-state-secret=...
  • -auth-google-client-id=...
  • -auth-google-client-secret=...
  • -auth-google-redirect-url=...
  • optional -auth-google-issuer-url=... (defaults to https://accounts.google.com)
  • optional -auth-google-scopes=openid,profile,email

Flow:

  1. GET /v1/auth/providers
  2. GET /v1/auth/login?provider=google
  3. GET /v1/auth/callback/google
  4. Server creates cookie session + CSRF cookie.

Session details:

  • Default cookie names:
    • session: guardian_session
    • csrf: guardian_csrf
  • Default session max age: 12h
  • Default idle timeout: 2h
  • Mutating endpoints require CSRF (header X-CSRF-Token must match CSRF cookie).

Set:

  • -auth-enabled=true
  • -auth-bearer-enabled=true
  • -auth-bearer-issuer-url=...
  • -auth-bearer-client-id=...
  • optional -auth-bearer-scopes=openid,profile,email

Flow:

  1. Tool calls GET /v1/auth/device-config.
  2. Tool discovers OIDC metadata from issuer (/.well-known/openid-configuration).
  3. Tool obtains OIDC ID token (device flow or equivalent).
  4. Tool calls control-plane with Authorization: Bearer <id_token>.

Verification model:

  • Bearer token is verified against issuer JWKS and expected audience (client_id).
  • This path expects an OIDC ID token.
  • Bearer principal is mapped to role member (not admin).
  • CSRF is not required for bearer-authenticated requests.

4) Auth Service Bearer for Governance Studio

Section titled “4) Auth Service Bearer for Governance Studio”

Set:

  • -auth-enabled=true
  • -auth-bearer-enabled=true
  • -auth-bearer-mode=auth_service
  • -auth-bearer-auth-service-base-url=...

The Auth Service base URL may be omitted when -adapter-auth-service-base-url is already configured. The bearer verifier uses the adapter URL as its fallback. If neither URL is available, control-plane fails configuration validation at startup.

Flow:

  1. Governance Studio sends its user token in Authorization: Bearer <token>.
  2. Control-plane forwards the token to Auth Service with POST /api/v1/auth/validate?include_claims=true.
  3. Auth Service validates the token and returns the resolved user, organization roles, and enriched claims.
  4. Control-plane constructs the bearer principal and applies its normal endpoint authorization rules.

Verification model:

  • Token signature, issuer, expiry, and other validity decisions are delegated to Auth Service rather than performed through local OIDC discovery.
  • The subject is read using the Auth Service-compatible fallback order: subject, sub, userId, then user_id.
  • Only the enriched claim https://governance.eqtylab.io/has_admin_access=true grants an admin role hint. Organization roles such as organization_owner are tenant-scoped and do not grant Gateway platform administration. Other validated users default to member.
  • A matching user stored in the control-plane auth store takes precedence over the role hint, including stored role overrides and disabled-user status.
  • Successful validations and definitive token rejections are cached for 30 seconds. Transient Auth Service failures are not cached and fail closed.
  • CSRF is not required for bearer-authenticated requests.
  • GET /v1/auth/device-config returns 404 because delegated tokens do not use the control-plane OIDC device-flow configuration.

This mode is opt-in. Omitting -auth-bearer-mode keeps the default oidc behavior and its issuer/client validation, so standalone deployments continue to verify bearer tokens locally.

Enable both:

  • auth.google.enabled=true
  • auth.bearer.enabled=true

Choose the bearer verifier independently:

  • omit auth.bearer.mode or set it to oidc for standalone OIDC verification;
  • set auth.bearer.mode=auth_service for delegated Governance Studio tokens.

Use cases:

  • Console users authenticate via browser session.
  • Tools and integrated clients authenticate through the selected bearer mode.
  • admin: full control-plane access, including user management and agent revoke/restore.
  • member: scoped agent visibility and onboarding operations.
  • GET /health: public.
  • GET /v1/auth/providers: public (auth config discovery).
  • GET /v1/auth/device-config: public when OIDC bearer auth is enabled and issuer/client metadata is configured; returns 404 in auth_service mode.
  • GET /v1/auth/login: public (starts browser auth flow).
  • GET /v1/auth/callback/{provider}: public (OIDC callback).
  • POST /v1/auth/logout: authenticated principal.
    • cookie session calls require CSRF.
  • GET /v1/me: authenticated principal.
  • GET /v1/users: admin only.
  • PATCH /v1/users/{id}: admin only.
    • cookie session calls require CSRF.
  • POST /v1/agent-registration-tokens: authenticated principal.
    • cookie session calls require CSRF.
    • bearer calls do not require CSRF.
  • GET /v1/agents: authenticated principal.
    • admin: all agents.
    • member: filtered to agents bound to the caller subject.
  • GET /v1/agents/{id}: authenticated principal.
    • admin: full access.
    • member: only if caller has active binding.
  • GET /v1/projects/{project_uuid}/agents: project-scoped agent list.
    • Governance Studio project: requires a bearer token authorized by Auth Service for view_project_data on the bound Governance project.
    • operator project: admin only.
    • Auth Service authorization failures and outages fail closed; an outage returns 503 Service Unavailable.
  • GET /v1/projects/{project_uuid}/agents/{agent_id}: project-scoped agent detail by Gateway agent ID or membership subject DID.
    • uses the same Governance Studio view_project_data or operator-admin authorization as the project list.
    • returns only identities derived from membership evidence for the requested project; local key detail is restricted to the matching membership subject DID.
  • Project-agent runtime reads under /v1/projects/{project_uuid}/agents/{agent_id}:
    • audit-exchanges, audit-exchanges/timeseries, audit detail, and request/response body routes;
    • plugin-events;
    • state-cids, state-cids/{state_cid}/blob, and parent-bound state-cids/{state_cid}/blobs/{cid}.
    • require view_project_data, a locally resolved Gateway agent, and Gateway-known membership evidence for the requested project. Historical access remains available when that known evidence is expired, revoked, or invalid.
    • return only records whose authoritative audit project_urn equals urn:uuid:{project_uuid} and whose local agent_id matches. Candidate-only, empty-project, and legacy rows are excluded.
    • state and plugin records join to audit records with the server-generated exchange_key; correlation IDs are returned only as tracing metadata.
    • the child-blob route returns a blob only when the authorized state attestation directly references that CID. There is no flat project CID endpoint.
  • POST /v1/agents/{id}/revoke: admin only (CSRF required for cookie session).
  • POST /v1/agents/{id}/restore: admin only (CSRF required for cookie session).
  • GET /v1/agents/{id}/status-events: admin only.

Endpoint: POST /v1/agent-registration-tokens

Request:

  • agent_name (required)
  • agent_handle (optional, deterministic handle)
  • agent_id is rejected (managed by control-plane)

Behavior:

  • Subject (sub) is taken from authenticated principal external subject.
  • If agent_handle is provided, deterministic agent_id is generated.
  • Otherwise, agent_id includes a random suffix.
  • Token TTL is currently 15 minutes.
  • Response command is a template using VIPER_PROVISIONING_TOKEN placeholder (raw token is returned only in registration_token).
  • -auth-enabled / CONTROL_PLANE_AUTH_ENABLED
  • -auth-registration-token-secret / CONTROL_PLANE_AUTH_REGISTRATION_TOKEN_SECRET
  • -auth-registration-token-issuer / CONTROL_PLANE_AUTH_REGISTRATION_TOKEN_ISSUER
  • -auth-allowed-email-domains / CONTROL_PLANE_AUTH_ALLOWED_EMAIL_DOMAINS
  • -auth-bootstrap-admin-email / CONTROL_PLANE_AUTH_BOOTSTRAP_ADMIN_EMAIL
  • -auth-cookie-domain / CONTROL_PLANE_AUTH_COOKIE_DOMAIN
  • -auth-cookie-secure / CONTROL_PLANE_AUTH_COOKIE_SECURE
  • -auth-session-max-age / CONTROL_PLANE_AUTH_SESSION_MAX_AGE
  • -auth-session-idle-timeout / CONTROL_PLANE_AUTH_SESSION_IDLE_TIMEOUT
  • -auth-ui-base-url / CONTROL_PLANE_AUTH_UI_BASE_URL
  • -auth-google-enabled / CONTROL_PLANE_AUTH_GOOGLE_ENABLED
  • -auth-state-secret / CONTROL_PLANE_AUTH_STATE_SECRET
  • -auth-google-issuer-url / CONTROL_PLANE_AUTH_GOOGLE_ISSUER_URL
  • -auth-google-client-id / CONTROL_PLANE_AUTH_GOOGLE_CLIENT_ID
  • -auth-google-client-secret / CONTROL_PLANE_AUTH_GOOGLE_CLIENT_SECRET
  • -auth-google-redirect-url / CONTROL_PLANE_AUTH_GOOGLE_REDIRECT_URL
  • -auth-google-scopes / CONTROL_PLANE_AUTH_GOOGLE_SCOPES
  • -auth-bearer-enabled / CONTROL_PLANE_AUTH_BEARER_ENABLED
  • -auth-bearer-mode / CONTROL_PLANE_AUTH_BEARER_MODE
    • oidc (default): verify locally through OIDC discovery and JWKS.
    • auth_service: delegate validation to Auth Service.
  • -auth-bearer-issuer-url / CONTROL_PLANE_AUTH_BEARER_ISSUER_URL
  • -auth-bearer-client-id / CONTROL_PLANE_AUTH_BEARER_CLIENT_ID
  • -auth-bearer-scopes / CONTROL_PLANE_AUTH_BEARER_SCOPES
  • -auth-bearer-auth-service-base-url / CONTROL_PLANE_AUTH_BEARER_AUTH_SERVICE_BASE_URL

issuer-url, client-id, and at least one scope are required when bearer auth is enabled in oidc mode. The Auth Service base URL is required in auth_service mode unless adapter-auth-service-base-url supplies it.

Helm Values (deploy/charts/guardian-stack)

Section titled “Helm Values (deploy/charts/guardian-stack)”

Auth values:

  • controlPlane.auth.enabled
  • controlPlane.auth.registrationTokenSecret
  • controlPlane.auth.registrationTokenIssuer
  • controlPlane.auth.google.*
  • controlPlane.auth.bearer.enabled
  • controlPlane.auth.bearer.mode (oidc or auth_service; empty defaults to oidc)
  • controlPlane.auth.bearer.issuerURL
  • controlPlane.auth.bearer.clientID
  • controlPlane.auth.bearer.authServiceBaseURL
  • controlPlane.auth.bearer.scopes

Governance Studio example:

controlPlane:
auth:
enabled: true
bearer:
enabled: true
mode: auth_service
# Optional when CONTROL_PLANE_ADAPTER_AUTH_SERVICE_BASE_URL is supplied,
# for example through controlPlane.extraEnv.
authServiceBaseURL: http://governance-platform-auth-service:8080

Secret keys used by control-plane auth secret:

  • stateSecret (required when auth.google.enabled=true)
  • registrationTokenSecret
  • googleClientID (when Google enabled)
  • googleClientSecret (when Google enabled)

OpenFleet now uses bearer OIDC device flow for onboarding:

  • openfleet auth login -> reads /v1/auth/device-config, performs OIDC device flow, stores local session.
  • openfleet agents create --guardian-onboard -> mints registration token via control-plane and passes provisioning data to runtime.
  • Runtime (OpenClaw + viper) generates DID key material and registers with llm-gateway using the provisioning token.
  • openfleet agents onboard <name> -> rotates provisioning token for an existing runtime-provisioned workload.

This aligns tool onboarding with the same identity/trust model used by control-plane and gateway.