Configuration

TL;DR — AuthPlane loads config in three layers, highest priority last: built-in defaults → YAML file → AUTHPLANE_* env vars. Everything you need to boot is optional (SQLite + auto-generated keys + no config file); everything you need for production is explicit and validated at boot. This page lists every section, every key, and every env var.

Precedence

  1. Defaults — sensible values for local development, built into the binary.
  2. YAML fileauthserver serve --config config.yaml.
  3. Environment variablesAUTHPLANE_* prefix; override YAML.

Env vars win because you want the same YAML file across environments with per-deployment overrides for secrets and issuer URLs.

Comma-separated list variables (like AUTHPLANE_OIDC_SCOPES=openid,email,profile) split on commas. Duration variables accept Go duration strings (10s, 5m, 1h, 24h). Bool variables accept true, false, 1, 0.

Minimal dev config

Zero config works — the binary starts on :9000 with SQLite, auto-generated signing keys, and no resource. The moment you need to accept a real MCP client, add a resource:

resources:
  - slug: my-mcp-server
    uri: http://localhost:3000/
    backend_kind: mint
    display_name: My MCP Server
    scopes:
      - name: tools/echo
        description: Echo tool

Or a single Mint resource via env vars:

export AUTHPLANE_RESOURCE_URI=http://localhost:3000/
export AUTHPLANE_RESOURCE_SCOPES=tools/echo
authserver serve

Minimal production config

Boot validation enforces these when server.issuer is not localhost:

server:
  issuer: https://auth.example.com

session:
  secret: "generate-a-32-byte-random-string"   # openssl rand -hex 32
  secure: true

admin:
  api_key: "generate-a-secure-api-key"          # openssl rand -hex 32

Anything missing here fails boot with a structured error — you cannot accidentally deploy a non-local AuthPlane with a random ephemeral session secret.


server

Public HTTP server settings.

server:
  issuer: http://localhost:9000     # Public URL — appears in AS metadata and JWT `iss`
  address: ":9000"                  # Listen address
  read_timeout: 30s
  write_timeout: 30s
  idle_timeout: 120s
  shutdown_wait: 10s                # Graceful shutdown drain
  allowed_origins: []               # CORS allowlist for browser MCP clients
Env varField
AUTHPLANE_SERVER_ISSUERserver.issuer
AUTHPLANE_SERVER_ADDRESSserver.address
AUTHPLANE_SERVER_READ_TIMEOUTserver.read_timeout
AUTHPLANE_SERVER_WRITE_TIMEOUTserver.write_timeout
AUTHPLANE_SERVER_IDLE_TIMEOUTserver.idle_timeout
AUTHPLANE_SERVER_SHUTDOWN_WAITserver.shutdown_wait
AUTHPLANE_SERVER_ALLOWED_ORIGINSserver.allowed_origins (comma-separated; * for all)

issuer appears in the /.well-known/oauth-authorization-server metadata and as the iss claim in every JWT. MCP clients use it for discovery. Must match the URL clients use to reach AuthPlane exactly — no trailing slash.

allowed_origins — empty by default. Browser-based MCP clients (MCP Inspector, Claude Desktop web) fail CORS preflight silently without it. For local dev set AUTHPLANE_SERVER_ALLOWED_ORIGINS=*; for production set an explicit allowlist. Server-to-server-only deployments may leave this empty.


storage

Persistence layer. Both backends implement the same interfaces — choose by driver.

storage:
  driver: sqlite                    # "sqlite" or "postgres"
  sqlite:
    path: data/authserver.db
    wal: true                       # WAL mode for concurrent reads
  postgres:
    dsn: "postgres://user:pass@localhost:5432/authserver?sslmode=require"
    max_conns: 25
    min_conns: 5
    max_conn_lifetime: 1h
    max_conn_idle_time: 30m
Env varField
AUTHPLANE_STORAGE_DRIVERstorage.driver
AUTHPLANE_STORAGE_SQLITE_PATHstorage.sqlite.path
AUTHPLANE_STORAGE_SQLITE_WALstorage.sqlite.wal
AUTHPLANE_STORAGE_POSTGRES_DSNstorage.postgres.dsn
AUTHPLANE_STORAGE_POSTGRES_MAX_CONNSstorage.postgres.max_conns
AUTHPLANE_STORAGE_POSTGRES_MIN_CONNSstorage.postgres.min_conns
AUTHPLANE_STORAGE_POSTGRES_MAX_CONN_LIFETIMEstorage.postgres.max_conn_lifetime
AUTHPLANE_STORAGE_POSTGRES_MAX_CONN_IDLE_TIMEstorage.postgres.max_conn_idle_time

SQLite is default and recommended for single-instance deployments. Pure Go via modernc.org/sqlite (no CGO). WAL mode enables concurrent reads while writes serialize. DB file is created automatically on first boot.

PostgreSQL is required for multi-instance (HA) deployments. Uses LISTEN/NOTIFY for cross-instance signing-key rotation. Run authserver migrate before first start.


signing

JWT signing keys and algorithms.

signing:
  algorithm: ES256                  # "ES256" (default) or "RS256"
  key_store: keyfile                # "keyfile", "postgres_key", or "vault_transit"
  key_path: data/keys               # Keyfile store: directory for PEM files
  vault_transit:                    # Only when key_store: vault_transit
    address: https://vault:8200
    token: ""                       # Static token (mutually exclusive with approle)
    mount: transit
    key_name: authserver-signing
    timeout: 10s
    approle:
      role_id: ""
      secret_id: ""
      mount: approle
Env varField
AUTHPLANE_SIGNING_ALGORITHMsigning.algorithm
AUTHPLANE_SIGNING_KEY_STOREsigning.key_store
AUTHPLANE_SIGNING_KEY_PATHsigning.key_path
AUTHPLANE_SIGNING_PG_ENCRYPTION_KEY_ENVsigning.postgres_key.encryption_key_env (Postgres store only)
AUTHPLANE_VAULT_ADDRsigning.vault_transit.address
AUTHPLANE_VAULT_TOKENsigning.vault_transit.token
AUTHPLANE_VAULT_TRANSIT_MOUNTsigning.vault_transit.mount
AUTHPLANE_VAULT_TRANSIT_KEY_NAMEsigning.vault_transit.key_name
AUTHPLANE_VAULT_TIMEOUTsigning.vault_transit.timeout
AUTHPLANE_VAULT_APPROLE_ROLE_IDsigning.vault_transit.approle.role_id
AUTHPLANE_VAULT_APPROLE_SECRET_IDsigning.vault_transit.approle.secret_id
AUTHPLANE_VAULT_APPROLE_MOUNTsigning.vault_transit.approle.mount

AlgorithmsES256 (ECDSA P-256) is default: compact tokens, fast verification. RS256 (RSA 2048) for environments that require it (legacy IDPs, HSM constraints).

Key storeskeyfile writes PEM keys to key_path. postgres_key (HA-safe) stores keys in the storage DB with encryption at rest — required when running multiple instances so key rotation propagates via LISTEN/NOTIFY. Note: postgres (without _key) fails boot; the suffix disambiguates from the storage driver of the same name. vault_transit delegates signing to HashiCorp Vault Transit — private keys never leave Vault. See Security: Key management and Operate: Vault Transit.

Key rotationauthserver admin key rotate generates a new signing key. The old key stays in the JWKS until it expires so in-flight tokens keep verifying.


dcr — Dynamic Client Registration (RFC 7591)

dcr:
  mode: open                        # "open", "approved_redirects", or "admin_only"
  approved_redirects:               # Used when mode: approved_redirects
    - http://localhost:*
    - https://inspector.mcp.garden/*
  rate_limit: 10                    # Registrations per second
  rate_limit_burst: 20
  default_token_expiry: 15m
  default_refresh_expiry: 168h      # 7 days
Env varField
AUTHPLANE_DCR_MODEdcr.mode
AUTHPLANE_DCR_APPROVED_REDIRECTSdcr.approved_redirects (comma-separated)
AUTHPLANE_DCR_RATE_LIMITdcr.rate_limit
AUTHPLANE_DCR_RATE_LIMIT_BURSTdcr.rate_limit_burst
AUTHPLANE_DCR_DEFAULT_TOKEN_EXPIRYdcr.default_token_expiry
AUTHPLANE_DCR_DEFAULT_REFRESH_EXPIRYdcr.default_refresh_expiry

Modes:

  • open — any client can register with any redirect URI. Development only.
  • approved_redirects — anyone can register, but the redirect URI must match a pattern in approved_redirects. Balanced for production with well-known MCP clients.
  • admin_only — clients must be pre-registered via the Admin API or CLI. Hardened default for internal-only deployments.

Rate limits apply per-IP to the /oauth/register endpoint. Defaults are conservative — bump for automated CI environments.


cimd — Client ID Metadata Document

Auto-registration via URL client_id (draft-ietf-oauth-client-id-metadata-document).

cimd:
  enabled: true
  require_https: true               # Require HTTPS for CIMD document URLs
  cache_ttl: 1h                     # Cache fetched documents for this long
  fetch_timeout: 10s                # HTTP timeout for fetching CIMD docs
Env varField
AUTHPLANE_CIMD_ENABLEDcimd.enabled
AUTHPLANE_CIMD_REQUIRE_HTTPScimd.require_https
AUTHPLANE_CIMD_CACHE_TTLcimd.cache_ttl
AUTHPLANE_CIMD_FETCH_TIMEOUTcimd.fetch_timeout

When a client presents a URL as its client_id, AuthPlane fetches and validates the metadata document at that URL. Set require_https: false only for local dev with http:// CIMD URLs.


session

Cookie-based session for the login and consent pages.

session:
  cookie_name: authserver_session
  max_age: 24h
  secure: false                     # Must be true in production
  same_site: lax                    # "lax", "strict", or "none"
  secret: ""                        # Required in production (32+ bytes)
Env varField
AUTHPLANE_SESSION_COOKIE_NAMEsession.cookie_name
AUTHPLANE_SESSION_MAX_AGEsession.max_age
AUTHPLANE_SESSION_SECUREsession.secure
AUTHPLANE_SESSION_SAME_SITEsession.same_site
AUTHPLANE_SESSION_SECRETsession.secret

The session secret signs and encrypts session cookies. For localhost dev, a random ephemeral secret is generated if none is set — sessions don’t survive restart. For production, set a stable 32+ byte value (openssl rand -hex 32).


rate_limit

Global rate limiting + brute-force protection on auth endpoints.

rate_limit:
  enabled: true
  requests_per_second: 100          # Global RPS limit
  burst: 200                        # Burst allowance
  auth_fail_max: 10                 # Max failed auth attempts per IP
  auth_fail_window: 10m             # Window for counting failures
  auth_lockout: 15m                 # Lockout duration after exceeding max
Env varField
AUTHPLANE_RATE_LIMIT_ENABLEDrate_limit.enabled
AUTHPLANE_RATE_LIMIT_RPSrate_limit.requests_per_second
AUTHPLANE_RATE_LIMIT_BURSTrate_limit.burst
AUTHPLANE_RATE_LIMIT_AUTH_FAIL_MAXrate_limit.auth_fail_max
AUTHPLANE_RATE_LIMIT_AUTH_FAIL_WINDOWrate_limit.auth_fail_window
AUTHPLANE_RATE_LIMIT_AUTH_LOCKOUTrate_limit.auth_lockout

Token-bucket global limit plus per-IP tracking of failed logins. Lockout applies to the /login endpoint only — OAuth endpoints stay open.


oauth

oauth:
  require_scope: true               # Require scope parameter in authorize requests
Env varField
AUTHPLANE_OAUTH_REQUIRE_SCOPEoauth.require_scope

When true (default), /oauth/authorize requests without a scope parameter are rejected with invalid_scope per RFC 6749 §3.3. When false, missing scope defaults to all registered scopes for the target resource — useful for MCP clients (like Claude Code at v0.1.x) that don’t send scope on /authorize. See ADR-012.

Optional grants — disabled by default

Three grants are off by default. Turn them on explicitly:

client_credentials:
  enabled: false                    # RFC 6749 §4.4
  token_expiry: 1h

dpop:
  enabled: false                    # RFC 9449
  nonce_ttl: 60s
  proof_lifetime: 60s
  require_nonce: false

token_exchange:
  enabled: false                    # RFC 8693
  allow_self_exchange: false
  max_chain_depth: 5
  token_expiry: 1h
Env varFieldDefault
AUTHPLANE_CLIENT_CREDENTIALS_ENABLEDclient_credentials.enabledfalse
AUTHPLANE_CLIENT_CREDENTIALS_TOKEN_EXPIRYclient_credentials.token_expiry1h
AUTHPLANE_DPOP_ENABLEDdpop.enabledfalse
AUTHPLANE_DPOP_NONCE_TTLdpop.nonce_ttl60s
AUTHPLANE_DPOP_PROOF_LIFETIMEdpop.proof_lifetime60s
AUTHPLANE_DPOP_REQUIRE_NONCEdpop.require_noncefalse
AUTHPLANE_TOKEN_EXCHANGE_ENABLEDtoken_exchange.enabledfalse
AUTHPLANE_TOKEN_EXCHANGE_ALLOW_SELF_EXCHANGEtoken_exchange.allow_self_exchangefalse
AUTHPLANE_TOKEN_EXCHANGE_MAX_CHAIN_DEPTHtoken_exchange.max_chain_depth5
AUTHPLANE_TOKEN_EXCHANGE_TOKEN_EXPIRYtoken_exchange.token_expiry1h
  • client_credentials — required for machine-to-machine flows and any SDK that performs server-to-server introspection. See Concepts: Grants & flows and Topologies: Backend service + MCP.
  • dpop — sender-constrained tokens. Additive: clients that don’t send a DPoP header still get bearer tokens (unless require_nonce: true locks it down). See Concepts: DPoP.
  • token_exchange — powers delegation, Broker resources, and Cross-App Access. Cross-client authorization is enforced per-resource via policy.exchange.allowed_client_ids on each registered resource. See Concepts: Delegation & act-chain.

XAA and JWT Bearer — YAML-only at v0.1.x

Enterprise-Managed Auth (Cross-App Access) is configured via YAML at v0.1.x. Env-var overrides are not exposed for this section.

xaa:
  enabled: false
  jwks_cache_ttl: 1h
  token_expiry: 1h
  max_assertion_age: 5m
  subject_mode: auto_map            # "auto_map" or "strict"
  require_resource: true            # reject assertions that don't specify `resource=`

Trusted IdPs, policies, and subject mappings are managed via the Admin REST API only — POST /admin/idps, POST /admin/xaa/policies, POST /admin/xaa/subject-mappings. There is no authserver admin xaa … CLI subcommand. See Concepts: Cross-App Access and Guides: Enterprise-Managed Auth.

agents

agents:
  enable_jwks_listing: false        # Publish agent JWK sets on /.well-known/agents.json
Env varField
AUTHPLANE_AGENTS_ENABLE_JWKS_LISTINGagents.enable_jwks_listing

Agent identity claims (agent_id, agent_chain) are emitted on every token by default. This flag controls the optional publishing of agent JWK sets for third-party verification.


admin

Admin API + Admin UI, on a separate port from public OAuth.

admin:
  enabled: true
  address: ":9001"                  # Separate port
  api_key: ""                       # Required in production
Env varField
AUTHPLANE_ADMIN_ENABLEDadmin.enabled
AUTHPLANE_ADMIN_ADDRESSadmin.address
AUTHPLANE_ADMIN_API_KEYadmin.api_key

Hosts both the REST API under /admin/* and the built-in React Admin UI at /admin/ui/. Authenticate API requests with Authorization: Bearer <api_key>. The UI uses the same key entered in-browser and stored in sessionStorage. Never expose :9001 to the public internet — keep it on an internal network or behind a firewall/NetworkPolicy.


oidc — Upstream OIDC Federation

oidc:
  enabled: false
  issuer: https://accounts.google.com
  client_id: ""
  client_secret: ""
  display_name: Google              # Button text on login page
  scopes: [openid, email, profile]
  redirect_uri: https://auth.example.com/oidc/callback
  show_local_login: true            # Show password form alongside OIDC button
  include_groups_scope: true        # Include "groups" scope if upstream supports it
  connector_id: ""                  # Dex connector_id parameter (optional)
Env varField
AUTHPLANE_OIDC_ENABLEDoidc.enabled
AUTHPLANE_OIDC_ISSUERoidc.issuer
AUTHPLANE_OIDC_CLIENT_IDoidc.client_id
AUTHPLANE_OIDC_CLIENT_SECREToidc.client_secret
AUTHPLANE_OIDC_DISPLAY_NAMEoidc.display_name
AUTHPLANE_OIDC_SCOPESoidc.scopes (comma-separated)
AUTHPLANE_OIDC_REDIRECT_URIoidc.redirect_uri
AUTHPLANE_OIDC_SHOW_LOCAL_LOGINoidc.show_local_login
AUTHPLANE_OIDC_INCLUDE_GROUPS_SCOPEoidc.include_groups_scope
AUTHPLANE_OIDC_CONNECTOR_IDoidc.connector_id

Validation: oidc.issuer and oidc.redirect_uri must use HTTPS in production; client_id, client_secret, and redirect_uri are required when enabled. See Guides: Federate to your IdP.


resources

Every MCP server (Mint) and every upstream provider target (Broker) is a resources entry.

resources:
  - slug: my-mcp-server
    uri: http://mcp-server:3000/mcp   # MCP canonical form — no trailing slash
    backend_kind: mint
    display_name: My MCP Server
    scopes:
      - name: tools/echo
        description: Echo a message
      - name: tools/query_database
        description: Query the database
    policy:
      exchange:
        allowed_client_ids: []        # Empty = any consented client
      runtime:
        client_ids: []                # Empty = no runtime binding
      connect:
        allowed_return_urls: []       # Overrides connect.allowed_return_urls
Env varField
AUTHPLANE_RESOURCE_URISingle Mint resource URI; slug auto-derived from host
AUTHPLANE_RESOURCE_SCOPESComma-separated scope names

Env vars support a single Mint resource. For multiple resources or any Broker, use YAML or the Admin API.

uri must match byte-for-byte what your MCP server publishes in its Protected Resource Metadata (RFC 9728). Compliant MCP clients echo the PRM resource field verbatim on /oauth/authorize and /oauth/token; AuthPlane uses exact string matching for audience validation. A one-character mismatch silently rejects every token. Use the MCP spec canonical form (no trailing slash, lowercase host, no default port). Watch for scheme/host case, default ports, percent-encoding — see Guides: Connect an MCP client.

backend_kindmint (AS issues its own JWT) or broker (AS vends an upstream provider token via RFC 8693). See Concepts: Architecture.

policy.exchange.allowed_client_ids — per-resource replacement for the legacy global cross-client allowlist. Empty means any consented client can exchange into this resource.

policy.runtime.client_ids — per-resource runtime binding: whitelist of clients whose tokens are accepted at THIS resource. See Guides: Runtime client binding.


observability

Logs, traces, metrics.

observability:
  logging:
    level: info                     # debug | info | warn | error
    format: json                    # json | text
    add_source: false               # Include file:line in log records
    outputs:
      stdout: true
      otel: false
      otel_endpoint: ""             # OTLP gRPC endpoint
      insecure: false               # Allow plaintext gRPC
  tracing:
    enabled: false
    endpoint: ""                    # OTLP gRPC endpoint (e.g. localhost:4317)
    insecure: false
    sample_rate: 1.0                # 0.0 to 1.0
  metrics:
    provider: prometheus            # prometheus | otel | both | none
    path: /metrics                  # Prometheus scrape endpoint
    otel_endpoint: ""               # OTLP endpoint when provider=otel or both
    insecure: false
Env varField
AUTHPLANE_LOG_LEVELobservability.logging.level
AUTHPLANE_LOG_FORMATobservability.logging.format
AUTHPLANE_LOG_ADD_SOURCEobservability.logging.add_source
AUTHPLANE_LOG_STDOUTobservability.logging.outputs.stdout
AUTHPLANE_LOG_OTELobservability.logging.outputs.otel
AUTHPLANE_LOG_OTEL_ENDPOINTobservability.logging.outputs.otel_endpoint
AUTHPLANE_LOG_OTEL_INSECUREobservability.logging.outputs.insecure
AUTHPLANE_TRACING_ENABLEDobservability.tracing.enabled
AUTHPLANE_TRACING_ENDPOINTobservability.tracing.endpoint
AUTHPLANE_TRACING_INSECUREobservability.tracing.insecure
AUTHPLANE_TRACING_SAMPLE_RATEobservability.tracing.sample_rate
AUTHPLANE_METRICS_PROVIDERobservability.metrics.provider
AUTHPLANE_METRICS_PATHobservability.metrics.path
AUTHPLANE_METRICS_OTEL_ENDPOINTobservability.metrics.otel_endpoint
AUTHPLANE_METRICS_INSECUREobservability.metrics.insecure

Full metric catalog and Prometheus setup in Guides: Monitoring and Reference: Metrics & CLI.


data_encryption

Encrypts sensitive data at rest — upstream refresh grants in broker_grants, and (when Postgres key store is enabled) signing keys.

data_encryption:
  driver: aes_master                              # "aes_master" or "vault_transit_encrypt"
  aes_master:
    key_env: AUTHPLANE_VAULT_ENCRYPTION_KEY       # Env var containing hex-encoded 256-bit key
    old_key_env: AUTHPLANE_VAULT_OLD_KEY          # Previous key for rotation (optional)
  vault_transit_encrypt:
    address: https://vault:8200
    auth_method: token                            # "token" or "approle"
    token_env: VAULT_TOKEN
    mount_path: transit
    key_name: authserver-data
    approle:
      role_id_env: VAULT_APPROLE_ROLE_ID
      secret_id_env: VAULT_APPROLE_SECRET_ID
Env varField
AUTHPLANE_DATA_ENCRYPTION_DRIVERdata_encryption.driver
AUTHPLANE_DATA_ENCRYPTION_KEY_ENVdata_encryption.aes_master.key_env
AUTHPLANE_DATA_ENCRYPTION_VAULT_ADDRESSdata_encryption.vault_transit_encrypt.address
AUTHPLANE_DATA_ENCRYPTION_VAULT_AUTH_METHODdata_encryption.vault_transit_encrypt.auth_method
AUTHPLANE_DATA_ENCRYPTION_VAULT_TOKEN_ENVdata_encryption.vault_transit_encrypt.token_env
AUTHPLANE_DATA_ENCRYPTION_VAULT_MOUNT_PATHdata_encryption.vault_transit_encrypt.mount_path
AUTHPLANE_DATA_ENCRYPTION_VAULT_KEY_NAMEdata_encryption.vault_transit_encrypt.key_name
AUTHPLANE_DATA_ENCRYPTION_VAULT_ROLE_ID_ENVdata_encryption.vault_transit_encrypt.approle.role_id_env
AUTHPLANE_DATA_ENCRYPTION_VAULT_SECRET_ID_ENVdata_encryption.vault_transit_encrypt.approle.secret_id_env
AUTHPLANE_VAULT_ENCRYPTION_KEYActual AES-256-GCM key (64-char hex string, referenced by key_env)
AUTHPLANE_VAULT_OLD_KEYPrevious AES key during rotation (referenced by old_key_env)

AES master key — local AES-256-GCM with HKDF-derived per-purpose subkeys. Generate: openssl rand -hex 32.

Vault Transit encrypt — delegates encryption to HashiCorp Vault Transit; plaintext never touches disk. Required for compliance environments.


broker_providers

Upstream OAuth / API-key / service-account providers that Broker resources reference. See Concepts: Token Vault.

broker_providers:
  - slug: github
    display_name: GitHub
    protocol: oauth
    config_data:
      client_id: "your-github-app-client-id"
      client_secret_ref: "CONNECTOR_GITHUB_SECRET"
      authorize_url: "https://github.com/login/oauth/authorize"
      token_url:     "https://github.com/login/oauth/access_token"
      response_format: standard
  - slug: slack
    display_name: Slack
    protocol: oauth
    config_data:
      client_id: "your-slack-app-client-id"
      client_secret_ref: "CONNECTOR_SLACK_SECRET"
      authorize_url: "https://slack.com/oauth/v2/authorize"
      token_url:     "https://slack.com/api/oauth.v2.access"
      response_format: form

No env-var shortcut — broker providers are managed via YAML or the Admin API/CLI (authserver admin provider create). The slug is the public ID; Broker resources reference it via broker_provider_slug.

protocol — one of oauth, apikey, service_account. Each dispatches to a different adapter under internal/brokerproto/.

Default scopes live on the Broker Resource row, not the provider — one provider can back multiple resources with different scope sets.


connect

The user-facing OAuth flow to grant your MCP server upstream access.

connect:
  state_secret: "generate-a-32-byte-random-string"   # HMAC key for state tokens
  allowed_return_urls:                                # Global default (per-resource override available)
    - http://localhost:*
    - https://myapp.example.com/*
  redirect_base_url: http://localhost:9000            # Base URL for OAuth callbacks
Env varField
AUTHPLANE_CONNECT_STATE_SECRETconnect.state_secret
AUTHPLANE_CONNECT_ALLOWED_RETURN_URLSconnect.allowed_return_urls (comma-separated)
AUTHPLANE_CONNECT_REDIRECT_BASE_URLconnect.redirect_base_url

state_secret — HMAC key for signing state tokens during the connect flow. Must be at least 32 characters (openssl rand -hex 32). Different key from session.secret.

allowed_return_urls — global default whitelist for connect-flow return URLs. Each resource can override via resources[].policy.connect.allowed_return_urls for finer control.


Validation rules enforced at boot

Boot fails with a structured error if any of these are violated:

  • session.secret required when server.issuer is not localhost
  • session.secure: true required when server.issuer is not localhost
  • admin.api_key required when admin.enabled: true and server.issuer is not localhost
  • oidc.client_id + client_secret + redirect_uri all required when oidc.enabled: true
  • oidc.issuer + oidc.redirect_uri must use HTTPS in production
  • Every enabled feature’s required-config combination is validated up-front (partial config → boot fails at startup, not at first request)
  • resources[].uri must be a well-formed URL

Config precedence in practice

Debug your effective config by comparing all three sources:

# See defaults + YAML
$ authserver serve --config config.yaml --dry-run

# See what env vars are set
$ env | grep AUTHPLANE_

# See the final self-check report in the boot log
$ authserver serve --config config.yaml 2>&1 | grep -A20 "feature self-check"

Every enabled feature logs a one-liner at boot indicating what config it picked up. Misconfigurations are fatal — the process exits with a structured error naming the missing keys.