| page_title | ory_project_config Resource - ory |
|---|---|
| subcategory | |
| description | Configures an Ory Network project's settings. |
Configures an Ory Network project's settings.
This resource manages the configuration of an Ory Network project, including authentication methods, password policies, session settings, CORS, and more.
-> Plan: Available on all Ory Network plans. Some configuration options may require specific plan features.
This resource supports drift detection — terraform plan will detect changes made outside of Terraform (e.g., via Ory Console or API) for any attributes you have configured.
~> Note: Only attributes present in your Terraform configuration are tracked for drift. Attributes you have not configured will not appear in plan output, even if they have non-default values in the API.
# Basic project configuration
resource "ory_project_config" "basic" {
cors_enabled = true
cors_origins = ["https://app.example.com"]
password_min_length = 10
session_lifespan = "720h0m0s" # 30 days
}
# Full security configuration
resource "ory_project_config" "secure" {
# Public CORS
cors_enabled = true
cors_origins = ["https://app.example.com", "https://admin.example.com"]
# Admin CORS
cors_admin_enabled = true
cors_admin_origins = ["https://admin.example.com"]
# Sessions
session_lifespan = "168h0m0s" # 7 days
session_cookie_same_site = "Strict"
session_cookie_persistent = true
# Password Policy
password_min_length = 12
password_identifier_similarity = true
password_check_haveibeenpwned = true
password_max_breaches = 0
# Authentication Methods
enable_password = true
enable_code = true
code_mfa_enabled = true # Enable code as a second factor for MFA
enable_oidc = true # Required for social providers (Google, GitHub, etc.)
enable_passkey = true
# Flow Controls
enable_registration = true
enable_recovery = true
enable_verification = true
# MFA
enable_totp = true
totp_issuer = "MyApp"
enable_webauthn = true
webauthn_rp_display_name = "MyApp"
webauthn_rp_id = "app.example.com"
webauthn_rp_origins = ["https://app.example.com"]
webauthn_passwordless = true
enable_lookup_secret = true
mfa_enforcement = "optional"
required_aal = "aal1"
# URLs
default_return_url = "https://app.example.com/dashboard"
allowed_return_urls = [
"https://app.example.com/dashboard",
"https://app.example.com/settings"
]
# Account Experience Branding
account_experience_name = "MyApp"
account_experience_logo_url = "https://cdn.example.com/logo.png"
account_experience_favicon_url = "https://cdn.example.com/favicon.ico"
account_experience_default_locale = "en"
# OAuth2 Token Lifespans
oauth2_access_token_lifespan = "1h0m0s"
oauth2_refresh_token_lifespan = "720h0m0s"
oauth2_auth_code_lifespan = "30m0s"
oauth2_id_token_lifespan = "1h0m0s"
oauth2_login_consent_request_lifespan = "30m0s"
# OAuth2 Strategies
oauth2_access_token_strategy = "jwt"
oauth2_jwt_scope_claim = "list"
oauth2_scope_strategy = "wildcard"
# OAuth2 PKCE
oauth2_pkce_enforced = false
oauth2_pkce_enforced_for_public_clients = true
# OAuth2 Claims
oauth2_allowed_top_level_claims = ["amr", "acr"]
oauth2_mirror_top_level_claims = false
# OAuth2 Issuer URL (custom issuer for OAuth2/OIDC tokens)
oauth2_issuer_url = "https://auth.example.com"
# OAuth2 Cookie Settings
oauth2_cookies_same_site_mode = "Strict"
oauth2_cookies_same_site_legacy_workaround = false
# Keto Namespaces (for fine-grained authorization)
keto_namespaces = ["documents", "folders", "groups"]
}
# Login style controls how authentication methods are presented.
# Default is "unified" (all methods on one screen).
# Use "identifier_first" to collect the identifier before showing auth methods.
resource "ory_project_config" "identifier_first" {
login_style = "identifier_first"
enable_password = true
enable_code = true
}
# Self-hosted UI configuration (custom login/registration pages)
resource "ory_project_config" "self_hosted_ui" {
login_ui_url = "https://auth.example.com/login"
registration_ui_url = "https://auth.example.com/registration"
recovery_ui_url = "https://auth.example.com/recovery"
verification_ui_url = "https://auth.example.com/verification"
settings_ui_url = "https://auth.example.com/settings"
error_ui_url = "https://auth.example.com/error"
enable_password = true
enable_registration = true
enable_recovery = true
enable_verification = true
}
# SMTP configuration for custom email delivery
resource "ory_project_config" "with_smtp" {
smtp_connection_uri = var.smtp_connection_uri
smtp_from_address = "noreply@example.com"
smtp_from_name = "MyApp"
smtp_headers = {
"X-SES-CONFIGURATION-SET" = "my-config-set"
}
enable_password = true
}
variable "smtp_connection_uri" {
type = string
sensitive = true
description = "SMTP connection URI (e.g., smtps://user:pass@smtp.example.com:465)"
}
# Native-only flows: explicitly clear browser return URLs
# Useful when the project only supports native (mobile/CLI) login flows
# and should not have any browser redirect URLs configured.
resource "ory_project_config" "native_only" {
default_return_url = ""
allowed_return_urls = []
enable_password = true
enable_code = true
}
# Session tokenizer templates (JWT tokenization for /sessions/whoami)
resource "ory_project_config" "with_tokenizer" {
session_tokenizer_templates = {
my_jwt = {
ttl = "1h"
jwks_url = "base64://eyJrZXlzIjpbXX0="
claims_mapper_url = "base64://bG9jYWwgcGF5bG9hZCA9IHN0ZC5leHRWYXIoJ3BheWxvYWQnLCB7fSk7CnsKICBzZXNzaW9uX2lkOiBwYXlsb2FkLnNlc3Npb24uaWQsCn0="
subject_source = "id"
}
short_lived = {
ttl = "5m"
jwks_url = "base64://eyJrZXlzIjpbXX0="
}
}
}
# Courier HTTP delivery (webhook-based email/SMS delivery)
resource "ory_project_config" "with_courier_http" {
courier_delivery_strategy = "http"
courier_http_request_config = {
url = "https://mail-api.example.com/send"
method = "POST"
body = "base64://ewogICJyZWNpcGllbnQiOiAge3sgLnJlY2lwaWVudCB9fSwKICAiYm9keSI6IHt7IC5ib2R5IH19Cn0="
auth = {
type = "basic_auth"
user = "mailuser"
password = var.mail_password
}
}
# Per-channel delivery (e.g., SMS via Twilio)
courier_channels = [
{
id = "sms"
request_config = {
url = "https://sms-api.example.com/send"
method = "POST"
body = "base64://ewogICJ0byI6IHt7IC5yZWNpcGllbnQgfX0sCiAgIm1lc3NhZ2UiOiB7eyAuYm9keSB9fQp9"
auth = {
type = "api_key"
name = "Authorization"
value = var.sms_api_key
in = "header"
}
}
}
]
}
variable "mail_password" {
type = string
sensitive = true
description = "Password for courier HTTP basic auth"
}
variable "sms_api_key" {
type = string
sensitive = true
description = "API key for SMS delivery service"
}Time-based attributes use Go duration strings. Examples:
| Duration | Meaning |
|---|---|
30m |
30 minutes |
1h |
1 hour |
24h0m0s |
24 hours |
168h |
7 days |
720h |
30 days |
8760h |
365 days |
Import using the project ID:
terraform import ory_project_config.main <project-id>After importing, if Terraform shows project_id forces replacement, ensure your configuration matches:
Option 1: Explicit project_id
resource "ory_project_config" "main" {
project_id = "the-exact-project-id-you-imported"
# ... other settings
}Option 2: Use provider default (recommended)
provider "ory" {
project_id = "the-exact-project-id-you-imported"
}
resource "ory_project_config" "main" {
# project_id inherits from provider
# ... other settings
}This resource supports CORS configuration for both public and admin endpoints:
- Public CORS (
cors_enabled,cors_origins) — Controls CORS for public-facing endpoints (login, registration, etc.) - Admin CORS (
cors_admin_enabled,cors_admin_origins) — Controls CORS for admin API endpoints
To explicitly clear default_return_url and allowed_return_urls (e.g., for native-only flows with no browser redirects), set them to empty values:
resource "ory_project_config" "native_only" {
default_return_url = ""
allowed_return_urls = []
}Omitting these attributes entirely (or setting them to null) leaves the existing API values unchanged.
- Project config cannot be deleted — it always exists for a project
- Deleting this resource from Terraform state does not reset the project configuration
- The
project_idattribute forces replacement if changed (you cannot move config to a different project) - After
terraform import, runterraform planto reconcile your configuration with the current API state
This resource exposes 75+ attributes across these configuration categories:
| Category | Examples |
|---|---|
| Password settings | min length, identifier similarity, max breaches, haveibeenpwned |
| Session settings | cookie same site, lifespan, whoami-required AAL |
| CORS | public and admin origins, enabled/disabled |
| Login flow | login style (unified, identifier_first) |
| Authentication | passwordless, code, OIDC (social sign-in), TOTP, passkey, WebAuthn, lookup secrets |
| OAuth2/Hydra | token lifespans, access token strategy, PKCE, claims, scope strategy, consent/login URLs |
| Recovery / Verification | enabled, methods, notify unknown recipients |
| Account enumeration | mitigation enabled |
| Keto | namespace configuration |
Some Ory project settings are not yet available through this resource. For settings not listed above, use one of these workarounds:
- Ory Console — console.ory.sh
- Ory CLI —
ory patch project --replace '/services/identity/config/...' - API —
PATCH /projects/{project_id}with JSON Patch operations
account_experience_default_locale(String) Default locale for the hosted login UI (e.g., 'en', 'de').account_experience_favicon_url(String) URL for the favicon in the hosted login UI.account_experience_logo_url(String) URL for the logo in the hosted login UI.account_experience_name(String) Application name shown in the hosted login UI.account_experience_stylesheet(String) Custom CSS stylesheet for the hosted login UI.allowed_return_urls(List of String) List of allowed return URLs.code_mfa_enabled(Boolean) Enable the code method as a second factor for MFA. When enabled, users can use one-time codes as a second authentication factor.cors_admin_enabled(Boolean) Enable CORS for the admin API.cors_admin_origins(List of String) Allowed CORS origins for the admin API.cors_enabled(Boolean) Enable CORS for the public API.cors_origins(List of String) Allowed CORS origins.courier_channels(Attributes List) Per-channel courier delivery configurations (e.g., SMS via Twilio). Each channel overrides the default delivery for a specific message channel. (see below for nested schema)courier_delivery_strategy(String) Courier delivery strategy: 'smtp' (default) or 'http'.courier_http_request_config(Attributes) HTTP request configuration for courier message delivery (used when courier_delivery_strategy is 'http'). (see below for nested schema)default_return_url(String) Default URL to redirect after flows.enable_code(Boolean) Enable code-based authentication.enable_lookup_secret(Boolean) Enable backup/recovery codes.enable_oidc(Boolean) Enable OIDC (OpenID Connect) social sign-in. Must be enabled for social providers (e.g. Google, GitHub) to work.enable_passkey(Boolean) Enable Passkey authentication.enable_password(Boolean) Enable password authentication.enable_recovery(Boolean) Enable password recovery flow.enable_registration(Boolean) Enable user registration.enable_totp(Boolean) Enable TOTP (Time-based One-Time Password).enable_verification(Boolean) Enable email verification flow.enable_webauthn(Boolean) Enable WebAuthn (hardware keys).error_ui_url(String) URL for the error UI.keto_namespaces(List of String) List of Keto namespace names to configure for Ory Permissions. Namespaces define the types of resources in your permission model (e.g., 'documents', 'folders'). Each namespace name must be unique.login_style(String) Login flow style: 'unified' (default) shows all auth methods on one screen, 'identifier_first' collects the identifier before showing auth methods.login_ui_url(String) URL for the login UI.mfa_enforcement(String) MFA enforcement level: 'none', 'optional', or 'required'.oauth2_access_token_lifespan(String) OAuth2 access token lifespan (e.g., '1h', '30m'). Requires Hydra service.oauth2_access_token_strategy(String) OAuth2 access token strategy ('jwt' or 'opaque').oauth2_allowed_top_level_claims(List of String) List of allowed top-level claims in OAuth2 access tokens (e.g., 'amr', 'acr').oauth2_auth_code_lifespan(String) OAuth2 authorization code lifespan (e.g., '30m'). Requires Hydra service.oauth2_consent_url(String) OAuth2 consent endpoint URL.oauth2_cookies_same_site_legacy_workaround(Boolean) Enable the SameSite=None legacy workaround for OAuth2 cookies. When enabled, a fallback cookie without SameSite is set alongside the main cookie for clients that don't support SameSite=None.oauth2_cookies_same_site_mode(String) SameSite attribute for OAuth2 cookies ('Lax', 'Strict', 'None').oauth2_error_url(String) OAuth2 error endpoint URL.oauth2_id_token_lifespan(String) OAuth2 ID token lifespan (e.g., '1h'). Requires Hydra service.oauth2_issuer_url(String) OAuth2 issuer URL. Overrides the default project URL used as the OAuth2/OIDC issuer.oauth2_jwt_scope_claim(String) How scopes are represented in JWT access tokens ('list', 'string', or 'both').oauth2_login_consent_request_lifespan(String) OAuth2 login/consent request lifespan (e.g., '30m'). Requires Hydra service.oauth2_login_url(String) OAuth2 login endpoint URL.oauth2_logout_url(String) OAuth2 logout endpoint URL.oauth2_mirror_top_level_claims(Boolean) Mirror top-level claims in OAuth2 ID tokens.oauth2_pkce_enforced(Boolean) Enforce PKCE for all OAuth2 clients.oauth2_pkce_enforced_for_public_clients(Boolean) Enforce PKCE for public OAuth2 clients only.oauth2_refresh_token_lifespan(String) OAuth2 refresh token lifespan (e.g., '720h' for 30 days). Requires Hydra service.oauth2_scope_strategy(String) OAuth2 scope matching strategy ('exact', 'wildcard').oauth2_session_encrypt_at_rest(Boolean) Encrypt OAuth2 sessions at rest.password_check_haveibeenpwned(Boolean) Check passwords against HaveIBeenPwned.password_identifier_similarity(Boolean) Check password similarity to identifier.password_max_breaches(Number) Maximum allowed breaches in HaveIBeenPwned.password_min_length(Number) Minimum password length.project_id(String) Project ID to configure. If not set, uses provider's project_id.recovery_ui_url(String) URL for the password recovery UI.registration_ui_url(String) URL for the registration UI.required_aal(String) Required Authenticator Assurance Level for protected resources: 'aal1' or 'aal2'.session_cookie_persistent(Boolean) Enable persistent session cookies (survive browser close).session_cookie_same_site(String) SameSite cookie attribute (Lax, Strict, None).session_lifespan(String) Session duration (e.g., '24h0m0s').session_tokenizer_templates(Attributes Map) JWT tokenizer templates for the /sessions/whoami endpoint. Each key is a template name, and the value configures how JWTs are generated. (see below for nested schema)session_whoami_required_aal(String) Required AAL for session whoami endpoint: 'aal1', 'aal2', or 'highest_available'.settings_ui_url(String) URL for the account settings UI.smtp_connection_uri(String, Sensitive) SMTP connection URI for sending emails.smtp_from_address(String) Email address to send from.smtp_from_name(String) Name to display as sender.smtp_headers(Map of String) Custom headers to include in emails.totp_issuer(String) TOTP issuer name shown in authenticator apps.verification_ui_url(String) URL for the verification UI.webauthn_passwordless(Boolean) Enable passwordless WebAuthn authentication.webauthn_rp_display_name(String) WebAuthn Relying Party display name.webauthn_rp_id(String) WebAuthn Relying Party ID (typically your domain).webauthn_rp_origins(List of String) Allowed WebAuthn origins.
id(String) Resource ID (same as project_id).
Required:
id(String) Channel identifier (e.g., 'sms').
Optional:
request_config(Attributes) HTTP request configuration for this channel. (see below for nested schema)
Required:
method(String) HTTP method (e.g., 'POST', 'PUT').url(String) Target URL for the HTTP request.
Optional:
auth(Attributes) Authentication configuration for the HTTP request. (see below for nested schema)body(String) Request body template. Supports base64:// scheme for Jsonnet templates.headers(Map of String) Additional HTTP headers to include.
Required:
type(String) Authentication type: 'basic_auth' or 'api_key'.
Optional:
in(String) Where to send the API key: 'header', 'cookie', or 'query'.name(String) Header/cookie/query parameter name for api_key auth.password(String, Sensitive) Password for basic_auth.user(String) Username for basic_auth.value(String, Sensitive) API key value for api_key auth.
Required:
method(String) HTTP method (e.g., 'POST', 'PUT').url(String) Target URL for the HTTP request.
Optional:
auth(Attributes) Authentication configuration for the HTTP request. (see below for nested schema)body(String) Request body template. Supports base64:// scheme for Jsonnet templates.headers(Map of String) Additional HTTP headers to include.
Required:
type(String) Authentication type: 'basic_auth' or 'api_key'.
Optional:
in(String) Where to send the API key: 'header', 'cookie', or 'query'.name(String) Header/cookie/query parameter name for api_key auth.password(String, Sensitive) Password for basic_auth.user(String) Username for basic_auth.value(String, Sensitive) API key value for api_key auth.
Required:
jwks_url(String, Sensitive) JWKS URL for signing tokens. Must use base64:// scheme (e.g., 'base64://eyJrZXlzIjpbXX0=').
Optional:
claims_mapper_url(String) Jsonnet claims mapper URL. Supports base64:// and https:// schemes.subject_source(String) Subject source for the JWT: 'id' (default) or 'external_id'.ttl(String) Token time-to-live duration (e.g., '1h', '30m'). Default: '1m'.