Configuration
ThunderID's behavior is controlled by a combination of default configurations and deployment-specific overrides. This page covers all available configuration options and how to customize them.
Configuration Systemβ
ThunderID uses a two-tier configuration system:
- Default Configuration β Provides sensible defaults for all settings. These are built into ThunderID.
- Deployment Configuration β Located at
repository/conf/deployment.yamlin your ThunderID installation directory. This file overrides specific defaults for your deployment.
Only settings you want to override need to be specified in deployment.yaml. All other values will use the built-in defaults.
ThunderID must be restarted after any configuration change.
Server Configurationβ
Controls the ThunderID server's network settings and identity.
| Setting | Default | Description |
|---|---|---|
server.hostname | localhost | The hostname or IP address the server binds to |
server.port | 8090 | Port the server listens on |
server.http_only | false | If true, disables HTTPS and uses HTTP only (not recommended for production) |
server.identifier | default-deployment | Unique identifier for this deployment instance |
Gate Client Configurationβ
Configures the connection to ThunderID Gate (the login UI).
| Setting | Default | Description |
|---|---|---|
gate_client.hostname | localhost | The hostname where ThunderID Gate is hosted |
gate_client.port | 8090 | Port where ThunderID Gate is accessible |
gate_client.scheme | https | Protocol scheme (http or https) |
gate_client.path | /gate | Base path for ThunderID Gate |
TLS Configurationβ
Controls HTTPS/TLS settings for secure communication.
| Setting | Default | Description |
|---|---|---|
tls.min_version | 1.3 | Minimum TLS version to accept (1.2 or 1.3) |
tls.cert_file | repository/resources/security/server.cert | Path to TLS certificate file |
tls.key_file | repository/resources/security/server.key | Path to TLS private key file |
ThunderID ships with a self-signed certificate for local development at repository/resources/security/server.cert. For production, replace it with a certificate from a trusted Certificate Authority.
Database Configurationβ
ThunderID uses three separate databases for different purposes. Each database can be configured independently.
Connection parameters are grouped under a type-specific sub-key (postgres, sqlite, or redis). Only type is a top-level field; all other settings belong under the matching sub-key.
Config Databaseβ
Stores identity provider configurations, applications, and authentication flows.
| Setting | Default | Description |
|---|---|---|
database.config.type | sqlite | Database type (sqlite or postgres) |
database.config.postgres.* β only read when database.config.type: postgres:
| Setting | Default | Description |
|---|---|---|
database.config.postgres.hostname | "" | Database server hostname |
database.config.postgres.port | 0 | Database server port |
database.config.postgres.name | "" | Database name |
database.config.postgres.username | "" | Database username |
database.config.postgres.password | "" | Database password |
database.config.postgres.sslmode | "" | SSL mode (disable, require, verify-ca, verify-full) |
database.config.postgres.max_open_conns | 500 | Maximum number of open connections |
database.config.postgres.max_idle_conns | 100 | Maximum number of idle connections |
database.config.postgres.conn_max_lifetime | 3600 | Maximum connection lifetime in seconds |
database.config.postgres.max_retries | 3 | Maximum retry attempts for transient errors |
database.config.postgres.min_retry_backoff_ms | 50 | Minimum delay before retrying in milliseconds |
database.config.postgres.max_retry_backoff_ms | 2000 | Maximum delay before retrying in milliseconds |
database.config.sqlite.* β only read when database.config.type: sqlite:
| Setting | Default | Description |
|---|---|---|
database.config.sqlite.path | repository/database/configdb.db | SQLite database file path |
database.config.sqlite.options | _journal_mode=WAL&_busy_timeout=5000&_pragma=foreign_keys(1) | SQLite connection options |
database.config.sqlite.max_open_conns | 500 | Maximum number of open connections |
database.config.sqlite.max_idle_conns | 100 | Maximum number of idle connections |
database.config.sqlite.conn_max_lifetime | 3600 | Maximum connection lifetime in seconds |
database.config.sqlite.max_retries | 3 | Maximum retry attempts for transient errors |
database.config.sqlite.min_retry_backoff_ms | 50 | Minimum delay before retrying in milliseconds |
database.config.sqlite.max_retry_backoff_ms | 2000 | Maximum delay before retrying in milliseconds |
Runtime Databaseβ
Stores runtime data like sessions, tokens, and temporary data. The runtime database supports three backend types: sqlite, postgres, and redis.
| Setting | Default | Description |
|---|---|---|
database.runtime.type | sqlite | Database type (sqlite, postgres, or redis) |
database.runtime.postgres.* β only read when database.runtime.type: postgres:
| Setting | Default | Description |
|---|---|---|
database.runtime.postgres.hostname | "" | Database server hostname |
database.runtime.postgres.port | 0 | Database server port |
database.runtime.postgres.name | "" | Database name |
database.runtime.postgres.username | "" | Database username |
database.runtime.postgres.password | "" | Database password |
database.runtime.postgres.sslmode | "" | SSL mode (disable, require, verify-ca, verify-full) |
database.runtime.postgres.max_open_conns | 500 | Maximum number of open connections |
database.runtime.postgres.max_idle_conns | 100 | Maximum number of idle connections |
database.runtime.postgres.conn_max_lifetime | 3600 | Maximum connection lifetime in seconds |
database.runtime.postgres.max_retries | 3 | Maximum retry attempts for transient errors |
database.runtime.postgres.min_retry_backoff_ms | 50 | Minimum delay before retrying in milliseconds |
database.runtime.postgres.max_retry_backoff_ms | 2000 | Maximum delay before retrying in milliseconds |
database.runtime.sqlite.* β only read when database.runtime.type: sqlite:
| Setting | Default | Description |
|---|---|---|
database.runtime.sqlite.path | repository/database/runtimedb.db | SQLite database file path |
database.runtime.sqlite.options | _journal_mode=WAL&_busy_timeout=5000&_pragma=foreign_keys(1) | SQLite connection options |
database.runtime.sqlite.max_open_conns | 500 | Maximum number of open connections |
database.runtime.sqlite.max_idle_conns | 100 | Maximum number of idle connections |
database.runtime.sqlite.conn_max_lifetime | 3600 | Maximum connection lifetime in seconds |
database.runtime.sqlite.max_retries | 3 | Maximum retry attempts for transient errors |
database.runtime.sqlite.min_retry_backoff_ms | 50 | Minimum delay before retrying in milliseconds |
database.runtime.sqlite.max_retry_backoff_ms | 2000 | Maximum delay before retrying in milliseconds |
database.runtime.redis.* β only read when database.runtime.type: redis:
| Setting | Default | Description |
|---|---|---|
database.runtime.redis.address | "" | Redis server address in host:port format, for example localhost:6379 |
database.runtime.redis.username | "" | Redis ACL username β leave empty if ACLs are not configured |
database.runtime.redis.password | "" | Redis password or ACL user password |
database.runtime.redis.db | 0 | Redis logical database index (0β15) |
database.runtime.redis.key_prefix | "" | Prefix applied to all Redis keys written by ThunderID, for example thunderid: |
database.runtime.redis.max_retries | 5 | Maximum retry attempts for transient Redis network and timeout issues |
database.runtime.redis.min_retry_backoff_ms | 10 | Minimum delay before Redis retries in milliseconds |
database.runtime.redis.max_retry_backoff_ms | 100 | Maximum delay before Redis retries in milliseconds |
database.runtime.redis.dial_timeout_ms | 5000 | Redis connection (dial) timeout in milliseconds |
database.runtime.redis.read_timeout_ms | 3000 | Redis read timeout in milliseconds |
database.runtime.redis.write_timeout_ms | 3000 | Redis write timeout in milliseconds |
Redis Connection Requirementsβ
- ThunderID requires Redis 6.0 or later.
- ThunderID connects to a single Redis node. Cluster mode and Sentinel mode are not currently supported.
- The default Redis port is
6379. Ensure the port is reachable from the ThunderID server. - ThunderID does not enforce TLS at the client configuration level. To encrypt traffic, place a TLS-terminating proxy in front of Redis and point
database.runtime.redis.addressat the proxy endpoint. - If your Redis deployment uses Access Control Lists (ACLs), create a dedicated user and grant the following commands:
GET,SET,DEL,EXPIRE,EVAL,EVALSHA. Setdatabase.runtime.redis.usernameanddatabase.runtime.redis.passwordaccordingly. - ThunderID calls
PINGat startup to verify connectivity. The process terminates if the Redis server is unreachable.
Database Retry Behaviorβ
ThunderID applies exponential backoff with jitter for transient failures on non-transactional SQL read operations (Query path). Retry behavior is configurable per database via max_retries, min_retry_backoff_ms, and max_retry_backoff_ms under the relevant postgres or sqlite sub-key.
- Retryable classes include transient connectivity errors (
ErrBadConn,ErrConnDone, connection reset/refused), timeouts, deadlocks (40P01), and PostgreSQL resource/availability states (53xxx,57P01,57P02,57P03, too-many-connections). - Non-retryable conditions include no-row outcomes (
ErrNoRows), syntax/validation issues, and other permanent SQL conditions. - Write operations executed via
Executeare not retried automatically to avoid accidental duplicate writes. If your write path is explicitly idempotent, add idempotency at the business layer (for example with deterministic IDs or upsert semantics). - Retry metrics are emitted through OpenTelemetry metric instruments (
thunderid_db_retry_attempts_total,thunderid_db_retry_backoff_seconds,thunderid_db_operation_seconds), which can be exported to Prometheus via your OpenTelemetry collector pipeline.
User Databaseβ
Stores user profiles and credentials.
| Setting | Default | Description |
|---|---|---|
database.user.type | sqlite | Database type (sqlite or postgres) |
database.user.postgres.* β only read when database.user.type: postgres:
| Setting | Default | Description |
|---|---|---|
database.user.postgres.hostname | "" | Database server hostname |
database.user.postgres.port | 0 | Database server port |
database.user.postgres.name | "" | Database name |
database.user.postgres.username | "" | Database username |
database.user.postgres.password | "" | Database password |
database.user.postgres.sslmode | "" | SSL mode (disable, require, verify-ca, verify-full) |
database.user.postgres.max_open_conns | 500 | Maximum number of open connections |
database.user.postgres.max_idle_conns | 100 | Maximum number of idle connections |
database.user.postgres.conn_max_lifetime | 3600 | Maximum connection lifetime in seconds |
database.user.postgres.max_retries | 3 | Maximum retry attempts for transient errors |
database.user.postgres.min_retry_backoff_ms | 50 | Minimum delay before retrying in milliseconds |
database.user.postgres.max_retry_backoff_ms | 2000 | Maximum delay before retrying in milliseconds |
database.user.sqlite.* β only read when database.user.type: sqlite:
| Setting | Default | Description |
|---|---|---|
database.user.sqlite.path | repository/database/userdb.db | SQLite database file path |
database.user.sqlite.options | _journal_mode=WAL&_busy_timeout=5000&_pragma=foreign_keys(1) | SQLite connection options |
database.user.sqlite.max_open_conns | 500 | Maximum number of open connections |
database.user.sqlite.max_idle_conns | 100 | Maximum number of idle connections |
database.user.sqlite.conn_max_lifetime | 3600 | Maximum connection lifetime in seconds |
database.user.sqlite.max_retries | 3 | Maximum retry attempts for transient errors |
database.user.sqlite.min_retry_backoff_ms | 50 | Minimum delay before retrying in milliseconds |
database.user.sqlite.max_retry_backoff_ms | 2000 | Maximum delay before retrying in milliseconds |
Cache Configurationβ
ThunderID includes both in-memory and Redis-backed caching to improve performance.
| Setting | Default | Description |
|---|---|---|
cache.disabled | false | If true, disables caching entirely |
cache.type | inmemory | Cache type (inmemory or redis). When cache.type is redis, ThunderID does not use the periodic cleanup routine. |
cache.size | 1000 | Maximum number of cache entries |
cache.ttl | 3600 | Default time-to-live for cache entries in seconds |
cache.eviction_policy | LRU | Cache eviction policy (LRU - Least Recently Used) |
cache.cleanup_interval | 300 | In-memory only. Interval in seconds to clean up expired entries. Not used when cache.type is redis. |
cache.properties[] | [] | Per-cache overrides for name, disabled, size, ttl, and eviction_policy |
cache.redis.address | "" | Redis server address in host:port format |
cache.redis.username | "" | Redis username |
cache.redis.password | "" | Redis password |
cache.redis.db | 0 | Redis database index |
cache.redis.key_prefix | thunderid | Prefix added to all Redis cache keys |
cache.redis.max_retries | 5 | Maximum retry attempts for transient Redis network and timeout issues |
cache.redis.min_retry_backoff_ms | 10 | Minimum delay before Redis retries in milliseconds |
cache.redis.max_retry_backoff_ms | 100 | Maximum delay before Redis retries in milliseconds |
cache.redis.dial_timeout_ms | 5000 | Redis connection (dial) timeout in milliseconds |
cache.redis.read_timeout_ms | 3000 | Redis read timeout in milliseconds |
cache.redis.write_timeout_ms | 3000 | Redis write timeout in milliseconds |
Cache Property Overridesβ
Use cache.properties when you want to override cache behavior for a specific internal cache instead of changing the global cache settings for all caches.
Each item in cache.properties supports these fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Internal cache name to override. The value must match the cache name exactly. |
disabled | No | Disables only that cache when set to true. |
size | No | Maximum number of entries for that cache. This setting applies to in-memory caches only. |
ttl | No | Time-to-live for that cache in seconds. |
eviction_policy | No | Eviction policy for that cache. Supported values are LRU and LFU. This setting applies to in-memory caches only. |
If you omit a field in a cache.properties entry, ThunderID falls back to the corresponding global cache.* setting.
That means you can override only the fields you need. ThunderID continues to use the global cache settings for any fields you leave unset.
ThunderID currently uses cache names such as:
ApplicationByIDCacheApplicationByNameCacheOAuthAppCacheFlowByIDCacheFlowByHandleCacheCertificateByIDCacheCertificateByReferenceCacheEntityTypeByIDCacheEntityTypeByNameCacheFlowGraphCache
FlowGraphCache is always in-memory. It caches process-local flow graph Go objects during flow execution, not shared system-level cache data.
When cache.type is redis, per-cache ttl and disabled remain useful. Per-cache size and eviction_policy do not affect Redis behavior because Redis manages memory and eviction independently.
Redis Cache Configurationβ
Set cache.type to redis and configure cache.redis.address to enable Redis-backed cache storage.
cache:
type: "redis"
ttl: 3600
redis:
address: "localhost:6379"
username: ""
password: ""
db: 0
key_prefix: "thunderid"
max_retries: 5
min_retry_backoff_ms: 10
max_retry_backoff_ms: 100
dial_timeout_ms: 5000
read_timeout_ms: 3000
write_timeout_ms: 3000
When Redis caching is enabled, ThunderID expires keys automatically based on TTL.
If ThunderID cannot connect to Redis during startup, it disables the cache layer.
JWT Configurationβ
Controls JWT (JSON Web Token) generation and validation.
| Setting | Default | Description |
|---|---|---|
jwt.validity_period | 3600 | Default JWT validity period in seconds (1 hour) |
jwt.audience | application | Default audience claim for JWTs |
jwt.preferred_key_id | default-key | Key ID to use for signing JWTs |
jwt.leeway | 30 | Clock skew tolerance in seconds for token validation |
OAuth Configurationβ
OAuth 2.0 and OpenID Connect settings.
| Setting | Default | Description |
|---|---|---|
oauth.refresh_token.renew_on_grant | false | If true, issues a new refresh token on each access token grant |
oauth.refresh_token.validity_period | 86400 | Refresh token validity period in seconds (24 hours) |
oauth.authorization_code.validity_period | 600 | Authorization code validity period in seconds (10 minutes) |
oauth.dcr.insecure | false | If true, allows insecure dynamic client registration (development only) |
oauth.allow_wildcard_redirect_uri | false | If true, allows wildcard patterns in registered redirect URIs: * and ** in the path component, and * in the host component (label-internal, alphanumeric only). When false, only exact redirect URI matching is performed and registering a wildcard URI returns a 400 Bad Request error. |
Enabling oauth.allow_wildcard_redirect_uri affects all applications in the deployment. See Use Wildcard Redirect URIs for pattern syntax and matching rules.
Flow Configurationβ
Authentication and registration flow settings.
| Setting | Default | Description |
|---|---|---|
flow.default_auth_flow_handle | default-basic-flow | Handle of the default authentication flow |
flow.user_onboarding_flow_handle | default-user-onboarding | Handle of the default user onboarding flow |
flow.max_version_history | 10 | Maximum number of flow versions to retain |
flow.auto_infer_registration | true | If true, automatically infers registration from authentication flows |
User Configurationβ
User management settings.
| Setting | Default | Description |
|---|---|---|
user.indexed_attributes | ["username", "email", "mobileNumber", "sub"] | User attributes that are indexed for fast lookups |
Declarative Resourcesβ
Controls declarative configuration support.
| Setting | Default | Description |
|---|---|---|
declarative_resources.enabled | false | If true, enables declarative resource configuration |
For Helm deployments, this setting is managed through declarativeResources.enabled in the Helm values. When enabled, the chart also mounts declarative resource files from either a ConfigMap or Secret into the ThunderID repository/resources directory.
Example Helm values:
declarativeResources:
enabled: true
# Base mount path inside the ThunderID container (default: /opt/thunder/repository/resources)
mountPath: /opt/thunder/repository/resources
# Mount as read-only (recommended)
readOnly: true
configMap:
name: declarative-resources
items:
# Format 1 β string shorthand: key and path are the same
- "identity_providers/google.yaml"
# Format 2 β explicit key/path mapping
- key: app1
path: applications/application1.yaml
# Format 3 β per-item mountPath override (absolute path)
- key: org-default
path: organization_units/default.yaml
mountPath: /opt/thunder/repository/resources/organization_units/default.yaml
To source files from a Kubernetes Secret instead (for resources containing sensitive values):
declarativeResources:
enabled: true
mountPath: /opt/thunder/repository/resources
readOnly: true
secret:
name: declarative-resources-secret
items:
- key: my-app-with-secrets
path: applications/my-app.yaml
Templates (email and other templated content) are now supported as a declarative resource. See the templates guide for schema, examples, and the configured directory location: Template declarative resources.
Resource Configurationβ
Authorization resource settings.
| Setting | Default | Description |
|---|---|---|
resource.default_delimiter | : | Default delimiter for resource hierarchies |
System Resource Serverβ
The system resource server controls administrative permissions for ThunderID Console operations such as managing users, groups, organization units, and user types. You can customize the handle and identifier to fit your deployment requirements.
| Setting | Default | Description |
|---|---|---|
resource.system_resource_server.handle | "" (empty) | Prefix added before all system permission strings. When empty, permissions use their base names (for example, system, system:ou). When set, the handle is added before every permission (for example, mgmt:system, mgmt:system:ou) |
resource.system_resource_server.identifier | system | Identifier for the system resource server. Can be set to a URI for RFC 8707 resource indicator flows (for example, https://api.example.com) |
Example:
resource:
system_resource_server:
handle: "mgmt"
identifier: "https://api.example.com"
With the handle set to mgmt, all system permissions change as follows:
| Default Permission | Custom Permission |
|---|---|
system | mgmt:system |
system:ou | mgmt:system:ou |
system:ou:view | mgmt:system:ou:view |
system:user | mgmt:system:user |
system:user:view | mgmt:system:user:view |
system:group | mgmt:system:group |
system:group:view | mgmt:system:group:view |
system:usertype | mgmt:system:usertype |
system:usertype:view | mgmt:system:usertype:view |
Update Console Scopesβ
When you change the system resource server handle, you must also update the ThunderID Console scopes to match the new permission strings. The scopes tell the Console which permissions to request during sign-in.
Update the scopes array in the Console configuration file for your deployment mode:
- Local development (
pnpm dev): Updatefrontend/apps/console/public/config.js. - Packaged build (
build.sh/setup.sh/setup.ps1): The setup scripts read the handle fromdeployment.yamland pass the value to the bootstrap process automatically. Updatefrontend/apps/console/public/config.jsfor the Console scopes. - Helm deployment: Update
configuration.consoleClient.scopesin your Helm values file (install/helm/values.yaml).
For example, with the handle set to mgmt, update the Console scopes array to:
scopes: [
'openid',
'profile',
'email',
'ou',
'mgmt:system',
'mgmt:system:user',
'mgmt:system:group',
'mgmt:system:ou:view',
'mgmt:system:usertype:view',
],
If the Console scopes do not match the configured handle, the admin user token will lack the required authorized_permissions claim. All administrative API calls from the Console will then return 403 Forbidden.
Observability Configurationβ
Monitoring and observability settings.
| Setting | Default | Description |
|---|---|---|
observability.enabled | false | If true, enables observability features |
observability.output.console.enabled | false | If true, outputs observability data to console |
observability.output.console.format | json | Console output format (json or text) |
observability.output.console.categories | ["observability.all"] | Observability categories to output |
Crypto Configurationβ
Cryptographic settings for encryption and signing.
Encryptionβ
| Setting | Default | Description |
|---|---|---|
crypto.encryption.key | file://repository/resources/security/crypto.key | Path to encryption key file |
Password Hashingβ
| Setting | Default | Description |
|---|---|---|
crypto.password_hashing.algorithm | PBKDF2 | Password hashing algorithm |
crypto.password_hashing.parameters.iterations | 600000 | Number of hashing iterations |
crypto.password_hashing.parameters.key_size | 32 | Derived key size in bytes |
crypto.password_hashing.parameters.salt_size | 16 | Salt size in bytes |
Signing Keysβ
Signing keys are configured as an array. Each key has the following properties:
| Setting | Description |
|---|---|
crypto.keys[].id | Unique identifier for the key |
crypto.keys[].cert_file | Path to certificate file |
crypto.keys[].key_file | Path to private key file |
Default Key:
- id: "default-key"
cert_file: "repository/resources/security/signing.cert"
key_file: "repository/resources/security/signing.key"
The key type under crypto.keys determines the algorithm in id_token_signing_alg_values_supported in the OIDC discovery document. RSA keys advertise RS256; ECDSA P-256, P-384, and P-521 keys advertise ES256, ES384, and ES512; Ed25519 keys advertise EdDSA. If multiple keys are configured, all resulting algorithms are included without duplicates.
Email Configurationβ
Controls email sending capabilities (e.g., for magic link authentication, user invitations).
| Setting | Default | Description |
|---|---|---|
email.smtp.host | "" | SMTP server hostname |
email.smtp.port | 0 | SMTP server port (e.g., 587 for STARTTLS) |
email.smtp.username | "" | SMTP authentication username |
email.smtp.password | "" | SMTP authentication password |
email.smtp.from_address | "" | Sender email address |
email.smtp.enable_start_tls | true | Enable STARTTLS encryption |
email.smtp.enable_authentication | true | Enable SMTP authentication |
Example:
email:
smtp:
host: "smtp.example.com"
port: 587
username: "yourUsername"
password: "yourPassword"
from_address: "yourEmail"
enable_start_tls: true
enable_authentication: true
Transport Mode: Referencing keys enable_start_tls and enable_authentication, only explicit STARTTLS is supported (set enable_start_tls: true). Implicit SMTPS (port 465) is not supported by this transport.
The email configuration is optional. If not provided, features that depend on email (e.g., magic link, user invitations) will not be available.
Authentication Provider Configurationβ
External authentication provider settings.
| Setting | Default | Description |
|---|---|---|
authn_provider.type | default | Provider type (default or rest) |
authn_provider.rest.base_url | "" | Base URL for REST authentication provider |
authn_provider.rest.timeout | 10 | Request timeout in seconds |
authn_provider.rest.security.api_key | "" | API key for REST provider authentication |
CORS Configurationβ
Cross-Origin Resource Sharing settings (typically defined in deployment.yaml).
| Setting | Default | Description |
|---|---|---|
cors.allowed_origins | [] | List of allowed origins. Each entry is either a literal origin string (scheme and host are case-insensitive, trailing dots are stripped, IDN labels are Punycode-encoded, IPv6 hosts are bracketed, ports are preserved verbatim β list each port variant separately) or an object of the shape { regex: "..." } whose RE2 pattern is matched against the raw Origin header. The special string "null" matches the CORS null origin. The literal "*" is rejected β list explicit origins or use a regex. Requests from any other origin are rejected. |
The methods, headers, credentials, and preflight cache TTL applied to each allowed response are configured per route in code; only the allowed-origins list is operator-tunable. Preflight responses default to a 600-second Access-Control-Max-Age.
Example:
cors:
allowed_origins:
- "https://app.example.com"
- "https://mΓΌnchen.example" # IDN β equal to its Punycode form
- "http://[::1]:8080" # IPv6 in bracketed form
- regex: '^https://[a-z0-9-]+\.staging\.example\.com(:[0-9]+)?$'
Regex patterns are taken as-is and matched against the raw Origin header byte-for-byte. Make patterns strict (anchor with ^ and $, escape . literals) so that a hostile origin cannot match them. Invalid patterns cause the server to fail at startup, and patterns that lack ^/$ anchors are flagged with a startup warning because they permit partial matches.
The null origin is shared by sandboxed iframes, file:// and data: documents, and some cross-origin redirects. Because multiple unrelated contexts all emit it, allowing "null" cannot identify the caller. List it only when those contexts are intentionally trusted β combined with credentialed responses, it lets any sandboxed page in any browser tab issue authenticated requests.
Passkey Configurationβ
WebAuthn/Passkey settings (typically defined in deployment.yaml).
| Setting | Description |
|---|---|
passkey.allowed_origins | Array of allowed origins for passkey operations |
Example:
passkey:
allowed_origins:
- "https://localhost:8090"
Security Configurationβ
Controls server-wide security behavior that is not specific to any single authenticator. Maps to SecurityConfig in the backend, nested under server.security.
| Setting | Default | Description |
|---|---|---|
server.security.jwks_cache_ttl | 300 | JWKS cache TTL in seconds. Applies to every JWKS consumer in the server (trusted issuer validation, federated OIDC authenticators such as Google, and so on). Fetched signing keys are reused from the in-process cache for this duration before being re-fetched. Plan external-server key rotations with at least this much overlap. Set to 0 to disable caching |
Trusted Issuer Configurationβ
Maps to TrustedIssuerConfig in the backend, nested under server.security.trusted_issuer. Setting server.security.trusted_issuer.issuer activates the feature: ThunderID trusts access tokens issued by an external authorization server and validates them against the external server's JWKS endpoint. When issuer is set, jwks_url and audience are required and ThunderID fails to start if either is missing. This is used for federated authentication scenarios where a central ThunderID instance issues tokens that tenant instances accept.
| Setting | Default | Description |
|---|---|---|
server.security.trusted_issuer.issuer | "" | Expected value of the token's iss claim. Must match the external authorization server's issuer URL exactly |
server.security.trusted_issuer.jwks_url | "" | URL of the external authorization server's JWKS endpoint used to fetch signing keys. Must use HTTPS (HTTP allowed only for localhost) |
server.security.trusted_issuer.audience | "" | Expected value of the token's aud claim. This should be this server's own identifier (typically its public URL) |
server.security.trusted_issuer.required_claims | [] | List of claims that every accepted token must contain. Each entry has a claim name and an expected value. If any required claim is missing or does not match, the token is rejected |
Example:
server:
security:
trusted_issuer:
issuer: "https://cloud.example.com"
jwks_url: "https://cloud.example.com/oauth2/jwks"
audience: "https://tenant.example.com"
required_claims:
- claim: "ouId"
value: "tenant-123"
The frontend must include a matching resource parameter in the authorization request so the external authorization server sets the token's aud claim to this server's identifier. See the Trusted Issuer guide for the end-to-end setup.