Changelog for Ory Hydra OEL
26.3.18
This version contains only minor changes and improvements such as dependency updates.
v26.3.17
Loopback redirect URIs now require a matching scheme
OAuth 2.0 loopback redirects (RFC 8252 section 7.3) with a dynamic port are now accepted only when the registered redirect URI
also uses the http scheme. Previously, a client registered with an https loopback redirect URI, such as
https://127.0.0.1/callback, also accepted plain http redirects to any port on that host.
Breaking changes
After upgrading, OAuth2 authorization requests fail redirect URI validation if the client registers only an https loopback URI
but the native app requests an http loopback URI with a dynamic port.
To restore the flow, update the OAuth2 client's redirect_uris so the registered URI uses the same scheme, loopback address,
path, and query as the URI requested by the app. The port may remain dynamic. For example, if the app requests
http://127.0.0.1:<port>/callback, register http://127.0.0.1/callback. For IPv6, register http://[::1]/callback.
26.3.16
This version contains only minor changes and improvements such as dependency updates.
v26.3.15
Oathkeeper adopts shared HTTP metrics and adds per-rule access decisions
Oathkeeper uses the shared Ory HTTP metric schema. It also exposes ory_oathkeeper_access_decisions_total, which reports allowed
requests, authentication failures, authorization failures, and pipeline errors by rule ID. Access decisions are independent of
login redirects and upstream response codes.
Breaking changes
For Oathkeeper dashboards and alerts, replace service with app and status_code with code. The request label is replaced
by endpoint, which contains API router patterns such as /rules/{param} and /decisions/ and is empty for proxy traffic. The
app values remain oathkeeper-api and oathkeeper-proxy. HTTP metrics also include version, hash, and buildTime. Metric
names retain the configured prefix. Health requests are now included.
serve.prometheus.hide_request_paths and serve.prometheus.collapse_request_paths are deprecated and have no effect. Update path
filters to use router patterns or the new per-rule counter.
In Oathkeeper, Kratos, Hydra, and Keto, the requests_statuses_total metric's method labels now follow the same normalization as
other shared HTTP metrics: recognized methods use lowercase values, and other methods use other. Oathkeeper applies this
normalization to all HTTP metrics.
Unsupported methods use other instead of unknown across all shared HTTP metrics. Update queries that filter on the previous
method label.
Require user authorization for verifiable credentials
POST /credentials issues credentials only to access tokens that represent an authenticated end user and carry both the openid
and userinfo_credential_draft_00 scopes. The same requirement applies to nonce priming requests. Both scopes must be granted
explicitly; a wildcard grant does not satisfy the requirement.
Breaking changes
A token that does not carry both scopes receives scope_not_granted with HTTP 403. Scope names are matched exactly, so a scope
that differs in case does not satisfy the requirement.
To use this draft endpoint, request both scopes in the authorization flow and grant both during consent. The token endpoint
returns c_nonce_draft_00 only for tokens that meet these requirements. Other access token issuance is unaffected.
Device authorization errors are shown on the error page
An OAuth 2.0 device authorization grant has no redirect URI, so an error at the device verification endpoint had nowhere to go and was written to the browser as a JSON response body. The most common case is a user who denies the request at the login or consent screen: they were left looking at raw JSON.
Those errors are now sent to the error page configured under urls.error, carried in the error and error_description query
parameters, the same way an authorization request that has no valid redirect URI already is. A denial arrives there under the
error code the rejection named, and the device still receives access_denied on its next poll.
If your error page distinguishes the errors it receives, expect device verification errors on it now. GET /oauth2/device/verify
no longer answers any error with a JSON body: every error path redirects to the error page instead. The device polling behavior is
unchanged.
A rejection also reads as one sentence now. Account Experience rejects a login or consent request with error_description set to
"The request was rejected. The application was not granted access to your account." and no longer sends error_hint, so OAuth 2.0
clients of a redirect-based flow receive that text in their error redirect.
Hydra gains a FIPS 140-3 build
Ory Enterprise License releases now include a dedicated FIPS build of Hydra (hydra_oel_fips_<version>_<os>_<arch>) next to the
standard build. The FIPS build uses the Go Cryptographic Module and starts with GODEBUG=fips140=on by default, so Hydra's
cryptography runs through the FIPS 140-3 validated module. The standard build is unchanged as long as it runs without
GODEBUG=fips140=on.
A few things need attention before you switch. The FIPS build seals transient payloads with AES-256-GCM instead of
XChaCha20-Poly1305, so the two builds cannot read each other's login, consent, device, and logout flows, verifiable credential
nonces, unredeemed authorization codes, or admin API page tokens; plan a stop-then-start cutover. hydra serve refuses to start
unless secrets.system, secrets.cookie, and secrets.pagination are each set explicitly with their own active secret, and
unless oauth2.hashers.algorithm is pbkdf2 — bcrypt hashes client secrets outside the validated module, though client secrets
already hashed with it keep verifying. And because AES-GCM draws a random nonce per payload, regular rotation of secrets.system
is mandatory in FIPS mode.
For the full picture — obtaining and verifying the build, the required configuration, the rotation budget, and what each cipher changes to — see FIPS 140-3 build.
TLS configuration and unix socket recovery in Hydra, Keto and Kratos
A configured certificate is now the only switch for TLS in all three services. serve.<endpoint>.tls.enabled is deprecated but
still accepted so that an existing configuration keeps loading, but it has no effect. Each service logs a warning at startup
naming the key to remove. To serve plain HTTP behind a proxy that terminates TLS, remove the certificate rather than the flag.
Hydra and Kratos also pick up two fixes from the shared serve configuration:
- configuring only one half of a certificate pair is now a startup error instead of a listener that silently falls back to plain HTTP
- a unix socket left behind by an unclean shutdown is replaced instead of blocking the next start.
Because the three services now configure their endpoints identically, the documentation for it is one page: Serving the APIs. It replaces the separate Hydra and Kratos HTTPS pages, which now redirect to it.
v26.3.14
Do not skip this release when upgrading without downtime
This release prepares an optimization to the OAuth 2.0 Device Authorization Grant. A later release completes it.
Deploy this release before that later one. If you run several Ory Hydra instances and upgrade them one at a time, skipping this release can leave a device that finishes signing in mid-upgrade with an access token but no ID token, and no error to explain it.
No action is needed if you run a single instance, or if you accept downtime during upgrades.
v26.3.13
Hydra no longer generates a self-signed TLS certificate
When TLS was enabled and no certificate was configured, Hydra generated a self-signed certificate at startup, stored it in the
hydra.https-tls JSON Web Key Set, and served it. Hydra now starts only with a certificate you provide. Enabling TLS without one
fails at startup with an error naming the settings to set.
What you must do
You are affected if you set serve.tls.enabled (SERVE_TLS_ENABLED) to true without setting a certificate. Choose one:
- Configure a certificate and private key, either as file paths (
serve.tls.cert.pathandserve.tls.key.path) or base64-encoded (serve.tls.cert.base64andserve.tls.key.base64). Hydra reloads the certificate when the file changes. - Disable
serve.tls.enabledif a load balancer, ingress controller, or service mesh terminates TLS in front of Hydra. Hydra then serves HTTP.
Deployments that already configure a certificate, and deployments that leave TLS disabled, are unaffected. Once Hydra starts, you
can delete the now-unused key set with hydra delete jwks hydra.https-tls.
Why
The generated certificate could not authenticate Hydra to anything, and generating it caused problems of its own:
- The only name in it was
localhost, with no IP addresses, so no client that verifies hostnames could accept it for a real deployment. - It was valid for 31 days and was never renewed. Because it was stored in the database and reused on every restart, any installation older than a month has been serving an expired certificate. Only peers that skip verification, such as load balancers that do not validate backend certificates, kept working — which is why this change can surface as a startup failure on an installation that appeared healthy.
- It was a certificate authority certificate that could sign certificates. Adding it to a trust store, as our documentation used to suggest, made Hydra's database a trust anchor for any name.
- Its private key was stored alongside your signing keys and could be read through the admin API.
- Replicas starting at the same time against a fresh database each generated their own certificate, so nodes behind the same load balancer served different ones. Also, a replica that lost the race writing its certificate back could fail to start.
Generating a certificate nothing can verify is not something Hydra should do on your behalf. A deployment that needs a self-signed certificate is better served by one you create, distribute to the clients that must trust it, and rotate on your own schedule.
Hydra no longer supports the TLS termination allow-list
The serve.tls.allow_termination_from setting is gone. A configuration file that still sets it fails validation at startup, with
an error naming the key. Set through SERVE_TLS_ALLOW_TERMINATION_FROM, it is ignored.
What you must do
You are affected if you set serve.tls.allow_termination_from in a configuration file. Remove the setting. Nothing else about how
Hydra serves traffic changes.
If a load balancer, ingress controller, or service mesh terminates TLS in front of Hydra, leave serve.tls.enabled unset. Hydra
then serves HTTP, and the proxy is responsible for HTTPS. Make sure the proxy is the only way to reach Hydra.
Why
The setting was meant to name the CIDR ranges allowed to reach Hydra over plain HTTP and to require an X-Forwarded-Proto: https
header from them. It never took effect. Hydra installed the check only when serve.tls.enabled was true, and a server that
speaks TLS never receives the plain HTTP requests the check was written to reject. The deployments the setting was written for —
the ones that terminate TLS at a proxy and therefore leave serve.tls.enabled unset — never ran it.
Deciding which callers may reach Hydra over plain HTTP belongs to the proxy or the network in front of it, which is where deployments that need it already enforce it.
Reusing a device code revokes its access and refresh tokens
Presenting a device_code that has already been exchanged now revokes the access and refresh tokens issued from that code,
including the ones the device obtained by refreshing. This is the handling
RFC 6749 section 4.1.2 defines for a reused authorization code, applied to
device codes. The reused request itself is still rejected with invalid_grant.
Ory can't tell whether the first or the second request came from your device, so it assumes the device_code leaked and withdraws
the tokens issued under it. Only the client the code was issued to can trigger this: a request from another client_id is
rejected without touching the tokens. Reuse is only recognized while the consumed device_code is still on record; a code
presented after that is rejected as unknown and revokes nothing.
Breaking change
Devices that retry the token request must stop reusing a device_code. A token request that timed out or whose response was
lost may still have issued tokens. Retrying with the same device_code now revokes them, leaving the device without working
credentials. Retry only after a failed request, treat invalid_grant as final, and start a new device authorization request
instead of polling on.
Cleaning up consumed device codes
Consumed device codes are retained briefly so reuse can be detected, then collected by hydra janitor --tokens. If you already
run the janitor on a schedule, no action is required.
OAuth2 client key sets refresh at most once every 15 seconds
When a client authenticates with private_key_jwt or sends a signed request object, Ory now starts at most one refresh attempt
every 15 seconds per jwks_uri for the key set downloaded from the client's jwks_uri, even when the assertion names an unknown
key ID. A failed attempt may be retried by the HTTP client within that refresh's timeout. A newly published signing key can stay
unknown to Ory for up to 15 seconds after the previous refresh. Publish a new key at jwks_uri at least 15 seconds before signing
assertions with it, and keep the retired key published while assertions signed with it can still arrive.
If refreshing the key set fails, Ory keeps serving the previously downloaded keys for up to one hour past their regular
three-minute lifetime before requests fail with the refresh error. Publishing an empty key set ({"keys": []}) revokes every key:
Ory adopts it on the next refresh and rejects all assertions immediately after.
v26.3.12
Validate OAuth 2.0 client grant types on creation and update
Ory Hydra now validates the grant_types field when you create or update an OAuth 2.0 client. Adding a grant type the server does
not implement — for example a typo like authorize_code — is rejected with an invalid_client_metadata error instead of silently
creating a client that cannot complete any flow. This applies to the admin API and to dynamic client registration. Matching folds
case, mirroring how the token endpoint matches a client's grant types.
Existing clients keep their grant types and stay fully maintainable through the API: updates, patches, lifespan changes, and secret rotation continue to work for clients that already carry an unrecognized grant type. Only newly added values are validated.
The OpenID Connect discovery document now also advertises the JWT bearer grant (urn:ietf:params:oauth:grant-type:jwt-bearer) in
grant_types_supported, matching what the server implements.
If you embed Hydra as a Go library and wire extra grant handlers through driver.WithExtraFositeFactories, also register their
grant types with driver.WithExtraGrantTypes; otherwise new clients can no longer be registered with those grant types.
Device authorization grant returns access_denied when the user denies the request
When a user denied a device authorization request (RFC 8628) at the login or consent screen, the device polling the token endpoint
never learned about the denial and kept receiving authorization_pending. The poll now returns access_denied as soon as the
user denies the request, so devices fail fast instead of appearing to hang. A denied user code can't be used again; the device has
to start a new flow.
Thanks to Pete Bacon Darwin for reporting the issue and contributing the consent-denial fix.
v26.3.11
Choose the new secret when rotating an OAuth 2.0 client secret
POST /admin/clients/{id}/secrets/rotate now accepts an optional request body with a client_secret field. Send a value to
rotate to that secret instead of a generated one:
{ "client_secret": "your-client-secret" }
The body is optional. Omit it and the endpoint behaves exactly as before, generating a random secret. The supplied secret must be at least 6 bytes. Ory generates 26-byte secrets, so a shorter supplied secret is weaker than a generated one.
Use this when the same client has to carry the same secret in more than one environment. Rotating in each environment separately produces different secrets, and setting a secret through the update or patch client APIs clears the rotated secrets, so the environments disagree about which previous secrets still work. Rotate in one environment, then rotate in the others with the value it returned: every environment ends up with the same current secret and the same grace window.
Ory does not check the supplied value against the client's current or rotated secrets. Supplying the current secret is treated as an ordinary rotation.
The error hint for too-short client secrets on the create, update, and patch client APIs now reads "at least 6 bytes long" instead of "at least 6 characters long", matching how the length is measured.
If you self-host with oauth2.hashers.algorithm set to bcrypt, client secrets longer than 72 bytes now return 400 Bad Request
explaining the limit. They previously returned 500 Internal Server Error. This affects creating, updating, patching, and
rotating clients. The default pbkdf2 algorithm has no length limit and is unchanged.
Breaking change in the TypeScript SDK
In @ory/client, the positional rotateOAuth2ClientSecret functions returned by OAuth2ApiFp and OAuth2ApiAxiosParamCreator
take the optional request body as their second argument, before options. Callers passing Axios options as the second argument
must move them to the third. The OAuth2Api class and OAuth2ApiFactory, which take a request-parameters object, are unchanged.
Device codes are now strictly single-use
A device_code can be redeemed only once. When several token requests present the same device_code at the same time, exactly
one receives tokens and the others are rejected with invalid_grant.
POST /oauth2/token also reports a device_code that can no longer be redeemed more accurately. It now answers 400 Bad Request
with invalid_grant, where it previously answered 500 Internal Server Error with server_error in some cases. A device that
retried on 5xx was retrying a device code that could never succeed. It now receives the terminal invalid_grant that RFC 8628
defines, and should start a new device authorization request. The same request also stops answering 500 Internal Server Error
when the user rejects the device request while the token request is in flight; it now answers access_denied. No configuration
changes are required.
Fix missing ID token in the device authorization grant
The token endpoint did not include an id_token in its response for the OAuth 2.0 device authorization grant (RFC 8628), even
when the openid scope was requested and granted. The response was otherwise successful, so the omission was easy to miss. Device
flows granted the openid scope now receive a standard OpenID Connect ID token. No action is required.
Symmetric keys no longer break public JSON Web Key responses
A JSON Web Key Set containing a symmetric key, for example an imported HS256 key, caused GET /.well-known/jwks.json to fail with
a 500 error when the set was listed in webfinger.jwks.broadcast_keys, and caused hydra get jwk --public to fail with an
encoding error.
A symmetric key has no public representation, so these public projections now omit it instead of failing: /.well-known/jwks.json
and hydra get jwk --public return the public halves of the remaining keys. For a set whose keys are all symmetric,
hydra get jwk --public prints an empty key list. No action is required.
v26.3.10
Dynamic client registration omits optional client metadata
The OpenID Connect dynamic client registration endpoints under /oauth2/register no longer return client_uri, logo_uri,
policy_uri, tos_uri, contacts, grant_types, response_types, and jwks when those fields have no value. RFC 7591 marks
them optional, and responses previously included them as an empty string, null, or an empty object.
This unblocks clients that check the registration response against a strict schema, where such a field is accepted only when it's
absent or correctly typed: an empty string isn't a valid URI, null isn't a valid array, and an empty object isn't a valid JSON
Web Key Set. Model Context Protocol (MCP) clients that register themselves through dynamic client registration failed on this.
Send a value for a field when you register or update a client to have it appear in the response.
The registration and update responses also no longer return metadata, matching the read response. metadata can't be set
through dynamic client registration, so sending the field back to PUT /oauth2/register/{id} — as RFC 7592 has a client do with
the values it received — was rejected with invalid_client_metadata.
The admin endpoints under /admin/clients, and the client object embedded in login, consent, and device request payloads, are
unchanged and still return these fields as empty-string, null, or an empty object.
Upgrading from Hydra 1.0.0-rc or earlier needs a stepped upgrade
hydra migrate sql up no longer converts the pre-1.0.0 migration tables (hydra_client_migration, hydra_jwk_migration,
hydra_oauth2_authentication_consent_migration, and hydra_oauth2_migration) into the schema_migration table.
This only affects a database whose last migration ran on Hydra 1.0.0-rc or earlier, meaning before June 2019. Every Hydra release
since 1.0.0 performed the conversion automatically on the first hydra migrate sql up and then dropped the old tables. If you
have run hydra migrate sql up at any point since June 2019, you are unaffected and need to do nothing.
Breaking changes
If your database still has the tables listed above, upgrading straight to this release fails during migration with an error such
as relation "hydra_client" already exists, because Hydra no longer recognizes the old tables and tries to apply the 2019
migrations from the beginning. No data is lost, but the upgrade does not complete.
To upgrade, back up your database, then run hydra migrate sql up once with any Hydra release from 1.0.0 (June 2019) up to the
previous release to convert the migration tables. Then upgrade to this release and run hydra migrate sql up again.
To check whether you are affected, confirm the old tables are gone:
SELECT table_name FROM information_schema.tables
WHERE table_name IN (
'hydra_client_migration',
'hydra_jwk_migration',
'hydra_oauth2_authentication_consent_migration',
'hydra_oauth2_migration'
);
An empty result means you are not affected.
Admin JWK endpoints can now withhold private key material
The new configuration value jwks.admin_api.expose_private_keys controls whether admin /keys responses include private key
material:
jwks:
admin_api:
expose_private_keys: false
With false, GET /admin/keys/{set}, GET /admin/keys/{set}/{kid} and POST /admin/keys/{set} omit private key material and
return the public key instead. Set this for defense in depth if you do not read private keys through the admin API, so that admin
API access alone does not expose the keys that sign your tokens.
The default is true, which keeps the previous behavior of returning private key material for software-backed keys.
Keys are always stored in full. A key with no public representation — a symmetric key — is omitted from the response entirely, so
a response can come back with an empty keys array.
The two PUT endpoints are unaffected by the setting: they echo the key material the request carried, which discloses nothing the
caller did not already hold.
Keys backed by a Hardware Security Module are always returned as public keys, regardless of this setting. The public
/.well-known/jwks.json endpoint is unaffected. It has always returned public keys only.
Because reads then return public keys only, writing such a response back unchanged would replace your signing keys with their
public halves. With false, PUT /admin/keys/{set} and PUT /admin/keys/{set}/{kid} therefore reject a request that would leave
the key set without any private key, and answer 400. Retiring one key to its public half still works while the set keeps another
private key, and DELETE /admin/keys/{set} still removes a set.
Concurrent client secret rotations no longer hand back a dead secret
Rotating an OAuth 2.0 client secret twice at the same time could return a secret that never worked. Both requests read the client,
added their own secret, and wrote the whole record back, so whichever request finished last erased the secret the other had just
handed to its caller. The caller received a 200 OK with a secret that failed every token request from then on.
Rotations of the same client are now serialized in the database, so every secret a rotation returns stays valid until it ages out of the five-secret retention window or you replace the client's secrets explicitly.
If many rotations pile up on the same client at once, a request may now fail with 409 Conflict instead of succeeding with an
unusable secret. Retry the request.
Consent revocation no longer times out on large databases
Revoking consent sessions with DELETE /admin/oauth2/auth/sessions/consent could time out when the OAuth2 token tables were
large. The revocation now uses index-backed queries whose cost scales with the number of sessions being revoked instead of the
size of the token tables. No action is required.
v26.3.9
Limit request bodies to 10 MB
Ory Hydra now rejects requests whose body is larger than 10 MB, on both the public and the admin API. Most endpoints respond with
400 Bad Request; endpoints that read the access token or client credentials from the request body, such as /userinfo, respond
with 401 Unauthorized instead.
Most endpoints were already bounded at the same magnitude, because Go's HTTP server caps form-encoded bodies at 10 MB. This change makes the limit explicit and closes the remaining gaps: multipart form data and JSON documents on the admin API were previously unbounded.
OAuth 2.0 and OpenID Connect requests are a few kilobytes at most, and so are the documents the admin API accepts, so this does
not affect normal traffic. The largest payloads you are likely to send are a private_key_jwt client assertion, a signed request
object, or an OAuth 2.0 client with an embedded JSON Web Key Set — all far below the limit.
Requests that exceed the limit now report the size as the reason. Previously an oversized form body was reported as an empty body, which pointed at the wrong cause.
Breaking changes
Integrations that send request bodies larger than 10 MB must reduce the body size before upgrading.
Expired JWT bearer grants no longer issue access tokens
The token endpoint no longer accepts urn:ietf:params:oauth:grant-type:jwt-bearer assertions for a trusted issuer grant whose
expires_at has passed. Previously, such assertions could still be accepted after the grant expired.
If an integration still exchanges assertions against an expired grant, those token requests now fail with invalid_grant.
Re-establish the trust relationship with a future expires_at to restore access. Deleted grants and unexpired grants behave as
before.
Self-hosted Ory Hydra and Ory Enterprise License users should upgrade to a version containing this fix and can revoke a trusted issuer immediately by deleting the grant. The fix is deployed across the Ory Network, and Ory Network users do not need to take action.
Reject browser-executable URL schemes on OAuth2 clients
Creating or updating an OAuth2 client now rejects redirect_uris, post_logout_redirect_uris, and frontchannel_logout_uri
values whose scheme a browser executes as script (javascript:, data:, vbscript:). Custom native-app schemes such as
com.example.app:/callback keep working.
Requests with such values now fail with 400 Bad Request, on the admin client APIs and on OpenID Connect dynamic client
registration. A rejected redirect_uris value returns an invalid_redirect_uri error; post_logout_redirect_uris and
frontchannel_logout_uri return invalid_client_metadata. These values never worked as OAuth2 endpoints, so no functioning
integration is affected.
26.3.8
This version contains only minor changes and improvements such as dependency updates.
v26.3.7
Prevent cross-client consent verifier redemption
An attacker who obtained a live consent verifier for another user's accepted flow could redeem it for a different OAuth 2.0 client. This could yield an authorization code for the attacker's client carrying the user's subject.
Exploitation required an accepted, unexpired, unconsumed consent verifier and the matching browser and CSRF context. An attacker could not use a verifier from their own flow. Obtaining the required verifier therefore depended on a separate compromise or leak in the trusted handoff between Hydra, the consent application, and the user's browser.
Hydra now binds the verifier to the OAuth 2.0 client resolved for the authorization request. Self-hosted Ory Hydra and Ory Enterprise License users should upgrade to a version containing this fix. The fix is deployed across the Ory Network, and Ory Network users do not need to take action.
Hydra also rejects a mismatched login verifier before recording authentication session state. The login-verifier path did not yield an authorization code because Hydra rejected the client mismatch later in the flow, but it could apply session or subject-mapping side effects before that rejection.
This change also allows POST authorization requests to carry all parameters in the URL-encoded request body. Hydra previously
rejected such requests with invalid_client after a login or consent verifier was issued.
26.3.6
This version contains only minor changes and improvements such as dependency updates.
v26.3.5
Add oauth2.grant_types_disabled config key
Ory Hydra's configuration schema now supports oauth2.grant_types_disabled: a list of OAuth 2.0 grant type identifiers clients
may not be registered with. An absent or empty list disables nothing, so deployments that never set the key are unaffected.
The key can only disable grant types; it cannot enable grant types the server does not implement. In particular, the Resource Owner Password Credentials grant remains unavailable in Ory Hydra OSS — it is implemented only in Ory Network and Ory Enterprise License (OEL) deployments.
Registering a new client with a disabled grant type, or adding a disabled grant type to an existing client, is rejected with
invalid_client_metadata (HTTP 400). Clients that already carry the grant type are unaffected: they keep working at runtime and
remain fully maintainable through the API — updates, secret rotation, and lifespan changes still succeed.
Disabled grant types are also omitted from grant_types_supported in the OpenID Connect discovery and OAuth 2.0 authorization
server metadata documents, so new integrations only see grant types they can register.
Hydra warns at startup about list entries that do not match a registered OAuth 2.0 grant type and are not custom extension grant
URIs — for example device_code instead of urn:ietf:params:oauth:grant-type:device_code, or a misspelled grant type URN — since
unknown entries disable nothing.
Set the key through an environment variable as a comma-separated list
(OAUTH2_GRANT_TYPES_DISABLED='password,urn:ietf:params:oauth:grant-type:device_code') or as a JSON array.
v26.3.4
hydra update oauth2-client no longer resets fields you did not pass
The hydra update oauth2-client command now updates only the fields you provide as flags. Previously, the command replaced the
entire client: any field not passed again on the command line was silently reset to its default value, which could drop redirect
URIs, secrets-related settings, CORS origins, and other configuration.
The command now sends a partial update (PATCH /admin/clients/{id}) built from exactly the flags you set. For example, running
hydra update oauth2-client <id> --name "new name" changes only the client name and leaves all other settings untouched.
Updating from a file (--file client.json or --file -) keeps the previous behavior and replaces the entire client, since a file
represents a complete client configuration.
Breaking changes
Scripts that relied on hydra update oauth2-client resetting unspecified fields to their defaults must now pass a complete client
definition with --file to get replace semantics.
Ory CLI
The equivalent change in the Ory CLI (ory update oauth2-client) will land in CLI v1.3.2.
Faster OAuth2 token issuance under database latency
Ory Hydra no longer serializes JSON Web Key reads behind a global lock. Token issuance, the authorize flow, and the OpenID Connect discovery endpoint now read signing keys concurrently, so a slow database query no longer queues all other requests behind it.
Generating a missing key set is now deduplicated per network and key set: concurrent requests that race on a fresh installation trigger exactly one key generation per instance and share its result. A request that is canceled while waiting no longer aborts the generation for other waiting requests.
The Ory Hydra OEL key-set cache now scopes cache entries by network ID.
Fix OIDC session leak in the device authorization grant
The OpenID Connect device grant now deletes its stored OIDC session after issuing an id_token. Previously the session was looked
up by the device-code signature but deleted using the raw device_code, so the delete never matched and every successful
device-flow id_token issuance left one OIDC session behind. On storage backends without a TTL this grew without bound.
This is an availability fix only. Device codes could still not be replayed, so there was no token-reuse or authorization impact.
Hydra CLI reports the key set name when getting JSON Web Keys
hydra get jwks (and ory get jwk in the Ory CLI) now reports which key set each key belongs to. Previously the set attribute
was missing from JSON and YAML output for single-key sets, and empty in every output format for sets with more than one key.
Each key in the output now carries its own set attribute. When keys from several sets are fetched in one command, the keys are
attributed to their correct sets instead of an empty name.
The API reference for creating a JSON Web Key now documents that generating a key into an existing set keeps the existing keys, except on deployments using a Hardware Security Module (HSM), where the set is replaced and contains only the new key.
Metrics endpoints now support OpenMetrics and trace exemplars
The Prometheus metrics endpoints of Kratos, Keto, Hydra, Oathkeeper, and Talos now additionally serve the OpenMetrics exposition format. The format is selected automatically through standard HTTP content negotiation: scrapers that ask for OpenMetrics receive it, and all other scrapers keep receiving the classic text format. No configuration or scraper change is necessary.
OpenMetrics enables exemplars, which attach a trace reference to a metric sample. Two sets of metrics use this:
- The HTTP request duration histograms and request counters attach an exemplar when a request runs under a sampled trace, so latency dashboards can link slow-request outliers and error spikes to the traces that caused them.
- The
ory_x_popx_cockroach_transaction_retries_totalcounter attaches an exemplar whose value is the transaction's retry count when a transaction is retried during a traced request, and the trace records adb.transaction.retryspan event. Dashboards can link retry spikes directly to the traces that caused them.
That counter's caller label now also more reliably attributes automatic CockroachDB transaction retries to the business
operation that opened the transaction, for example the OAuth2 refresh token flow, instead of a generic persistence Transaction
wrapper method. This makes it possible to see which operation is causing database contention. Dashboards or alerts that match
specific caller label values may need updating, because existing label values change with this release.
OAuth2 client secret rotation
Hydra now supports zero-downtime client secret rotation. See the documentation for details.
Thanks to @SamuelDev (https://github.com/SamuelDev) for the initial implementation!
YugabyteDB support for Kratos, Hydra, and Keto (OEL)
Ory Enterprise License deployments can now run Ory Kratos, Ory Hydra, and Ory Keto against YugabyteDB.
Enabling YugabyteDB
Point the dsn at your YugabyteDB cluster using the yugabyte:// scheme (the synonym yugabytedb:// also works):
yugabyte://user@host:5433/database?sslmode=disable
Recovering an interrupted migration
YugabyteDB applies DDL with autocommit. Old PostgreSQL migrations inherited by YugabyteDB can contain multiple statements, so an
interruption may leave the first statements applied without a row in schema_migration. Current versions of Ory no longer use
multi-statement migrations and are safe.
26.3.3
This version contains only minor changes and improvements such as dependency updates.
v26.3.2
Enforce a DNS-label boundary on wildcard return URLs and CORS origins
A wildcard in a return URL or CORS origin is now honored only when the * is confined to a subdomain label and the host part
after it is a registrable domain (an eTLD+1, such as https://*.example.com or https://*.example.co.uk). The wildcard then
matches only hosts under that one customer-owned domain.
These forms are no longer honored, because each can match an attacker-registrable host:
- dot-less, e.g.
https://*foo.com(matcheshttps://evilfoo.com); - public-suffix base, e.g.
https://*.com,https://*.co.uk,https://*.vercel.app; - trailing or bare, e.g.
https://www.example.*,https://*.
Only the host is checked: wildcards in the URL path are unaffected. Exact origins, the catch-all *, and Hydra's https://*
("all hosts for a scheme") are unchanged.
What enforces it
- Return URLs: Kratos drops unsafe wildcard
allowed_return_urlsentries when it loads its config, and they never match. - CORS origins: matching across Kratos, Hydra, Keto, Oathkeeper, and Talos enforces the same boundary at request time.
Breaking changes
Replace any unsafe wildcard with the labelled https://*.example.com form, or list exact origins. Update
serve.*.cors.allowed_origins and Kratos allowed_return_urls before upgrading; otherwise cross-origin requests and post-flow
redirects from those hosts will be blocked.
v26.3.1
JWT bearer grant no longer copies the assertion audience by default
The urn:ietf:params:oauth:grant-type:jwt-bearer grant no longer copies the aud (audience) claim of the assertion JWT into the
resulting access token by default. Per RFC 7523, the assertion's aud identifies the authorization server as the intended
recipient; it is not meant to define the audience of the issued access token.
A new setting, oauth2.grant.jwt.omit_assertion_audience, controls this behavior. It defaults to true (omit). Set it to false
to restore the previous behavior of copying the assertion audience into the access token.
Breaking changes
- Self-hosted Ory Hydra (OSS) and Ory Enterprise License (OEL): after upgrading, the assertion audience is omitted from access
tokens by default. Set
oauth2.grant.jwt.omit_assertion_audience: falseif you rely on the old behavior. - New Ory Network projects: created with the assertion audience omitted.
- Existing Ory Network projects: unchanged — they keep copying the assertion audience. To adopt the new behavior, set the option explicitly.
26.3.0
This version contains only minor changes and improvements such as dependency updates.
v26.2.22
Much faster OAuth2 logout
This change improves performance of OAuth2/OpenID Connect logout by moving to a database-free implementation. Logout challenges and verifiers are now self-contained encrypted tokens instead of database rows, and the lookup of clients registered for front- and back-channel logout uses a single indexed query.
The behavior of APIs and user interactions are unchanged. The admin endpoint GET /admin/oauth2/auth/requests/logout no longer
returns 410 Gone for already-handled logout requests, because handled state no longer exists.
Upgrade notes:
- Run SQL migrations before rolling out this version. The new client lookup query requires the index added by this release; on CockroachDB the query fails if the index is missing.
- Logout flows that are in flight while the upgrade rolls out fail with "logout request expired". Users retrying complete normally on the new version.
Revoke login sessions by subject in batches
Revoking all login sessions for a subject through DELETE /admin/oauth2/auth/sessions/login?subject=... now deletes the sessions
in bounded batches instead of a single statement.
Previously, a subject with a very large number of login sessions produced a single, very large delete that could exceed the database's transaction limits or time out, failing with a "context canceled" error and leaving the sessions in place. Deleting in batches keeps each transaction small, so the request completes reliably regardless of how many sessions a subject has.
The endpoint's behavior is unchanged for callers: all matching login sessions are removed. If the request is canceled partway through, the sessions deleted so far stay deleted and the call can be safely retried to finish the rest.
26.2.21
This version contains only minor changes and improvements such as dependency updates.
26.2.20
This version contains only minor changes and improvements such as dependency updates.
26.2.19
This version contains only minor changes and improvements such as dependency updates.
26.2.18
This version contains only minor changes and improvements such as dependency updates.
26.2.17
This version contains only minor changes and improvements such as dependency updates.
v26.2.16
Accept the OpenID Connect prompt=select_account value
OAuth 2.0 authorization requests that include prompt=select_account are now accepted instead of being rejected with an
invalid_request error. This brings support for all OpenID Connect prompt values defined in the specification.
Because a login session is tied to a single account, select_account is treated like login: the user is always sent to the
login screen, where they can authenticate with the account of their choice.
26.2.15
This version contains only minor changes and improvements such as dependency updates.
26.2.14
This version contains only minor changes and improvements such as dependency updates.
v26.2.13
Detect mismatched database dialect at startup
Kratos, Hydra, Keto, and backoffice now verify at startup that the configured database dialect matches the actual database server.
The migrate sql and serve commands run a single SELECT version() query and refuse to start if the DSN scheme does not match
the server.
Previously, pointing a service at a CockroachDB cluster with a postgres:// DSN (or the reverse) silently applied the wrong
migrations and produced broken schemas. This is easy to do because CockroachDB speaks the PostgreSQL wire protocol and advertises
a postgres:// DSN itself.
If your DSN scheme is wrong, you will now see a clear error such as:
DSN scheme postgres:// declares a PostgreSQL database but the server is
CockroachDB. Replace the scheme with cockroach:// so that the service
picks the correct migrations and SQL dialect.
To fix it, change the DSN scheme from postgres:// to cockroach:// (or the reverse) and restart. MySQL and SQLite are
unaffected.
26.2.12
This version contains only minor changes and improvements such as dependency updates.
26.2.11
This version contains only minor changes and improvements such as dependency updates.
v26.2.10
Don't log spurious errors in Hydra OAuth 2.0 flows
This change removes spurious "the OAuth 2.0 Authorization request must be aborted" messages from Hydra's logs, which do not indicate an error but simply that the user has been redirected.
v26.2.9
Patch security vulnerabilities in dependencies
Bump several dependencies to patched versions to address security advisories reported by Dependabot.
Notable updates:
github.com/jackc/pgx/v5tov5.9.2across all Go modules (SQL injection via placeholder confusion in dollar-quoted string literals).github.com/moby/spdystreamtov0.5.1(denial of service on container runtime interface).go.opentelemetry.io/oteltov1.41.0(remote DoS amplification via multi-value baggage header).postcssto>=8.5.10(XSS via unescaped</style>in CSS stringify output).uuidto>=14.0.0(missing buffer bounds check in v3/v5/v6 generators).@xmldom/xmldomto>=0.8.13(XML node injection and uncontrolled recursion).axios,follow-redirects,lodash,picomatch,brace-expansion,serialize-javascript,yaml,file-type,i18next-fs-backend,@nestjs/coreto their respective patched versions.
v26.2.8
Fix 409 Conflict errors on fresh CockroachDB v26.1 installs
Fresh Hydra installs on CockroachDB v26.1 returned a
409 Conflict: Unable to insert or update resource because a resource with that value exists already error on the first request
to /.well-known/jwks.json after running migrations. The error blocked Hydra from auto-generating its JSON Web Key Sets, which in
turn prevented OAuth token verification by relying parties.
Only fresh installs are affected. Existing deployments that ran the initial migrations on an earlier CockroachDB version and later upgraded their cluster to v26.1 are not affected, because the problematic behavior happens at migration time rather than at cluster upgrade time. Deployments on PostgreSQL, MySQL, or SQLite are also unaffected.
A new CockroachDB-only migration drops both phantom indexes if they are present. No operator action is required beyond applying migrations.
SSRF protection improvements
Error messages originating from the SSRF protection mechanism no longer leak IP addresses if the hostname resolves to an internal IP address. This prevents SSRF recon through user-supplied URLs and hostnames.
v26.2.7
New indexes on Hydra OAuth 2.0 token tables
Hydra adds (nid, subject, client_id) indexes to hydra_oauth2_access and hydra_oauth2_refresh. The indexes are created online
on CockroachDB and PostgreSQL, so applying the upgrade does not block reads or writes against the token tables during the schema
change.
On Hydra OEL and Hydra Cloud, the new indexes accelerate consent session revocation, complementing recent code-level improvements to the OEL revoke flow.
26.2.6
This version contains only minor changes and improvements such as dependency updates.
v26.2.5
Fix shared mutable state in error handling
Error globals such as herodot.ErrNotFound were package-level variables shared across all requests. Calling methods like
WithReason or WithDetail mutated these variables in place and returned the same pointer, so any request that added context to
an error — reason text, details, etc, modified the global. The next request to reach an error path using the same error inherited
those stale details.
As a consequence, observability (logs, traces) for requests resulting in an error suffered from the same issue: some errors were reported with details belonging to an unrelated request, or with fields missing that should have been present.
The new API creates a fresh error instance on each call, so each request gets its own copy.
The following values were at risk of leaking into unrelated error responses:
- HTTP cookie names (Kratos CSRF flow)
- Entity UUIDs (identity, organization, etc)
- OAuth2 error hints (Hydra and Kratos Hydra bridge)
- OIDC provider URLs and raw upstream error responses (Kratos OIDC strategy)
- External schema fetch URLs and HTTP status codes (Kratos schema handler)
- JWT claims and issuers (Oathkeeper JWT authenticator)
No data was written to persistent storage or transmitted outside the error response. Any two requests hitting the same error path on the same node — even back-to-back with no concurrency — could exchange error details.
Under concurrent load, the shared writes also constitute a true data race, which can additionally produce errors in an inconsistent or partially written state.
This change has no externally observable effect other than fixing the information leak in error paths.
v26.2.4
Add oauth2.preserve_ext_claims configuration option
When mirror_top_level_claims is disabled, custom claims not promoted to the top level of the access token were silently dropped.
The new oauth2.preserve_ext_claims setting (default false) keeps those claims in the ext object instead.
Migrate Helm chart repository URL from k8s.ory.sh to k8s.ory.com
The Helm chart repository URL has been updated from k8s.ory.sh to k8s.ory.com. The old URL will continue to work with
redirects for a limited time. Update your Helm repository configuration to use the new URL.
v26.2.3
Remove CGO requirement from Ory by switching to modernc.org/sqlite
Migrates SQLite support from mattn/go-sqlite3 (CGO) to modernc.org/sqlite (pure Go) and fixes several timestamp comparison
bugs that caused incorrect pagination results. This migration is fully backwards compatible. Going forward, the -tags sqlite tag
is no longer required to compile Ory.
26.2.2
This version contains only minor changes and improvements such as dependency updates.
v26.2.1
Accept custom domain issuer as valid audience in JWT Bearer Grant
When using a custom domain (CNAME) as the OAuth2 issuer URL, Hydra now accepts the issuer-derived token URL as a valid audience in JWT Bearer Grant assertions. Previously, only the internal public URL was accepted, causing JWT Bearer Grant requests to fail when clients set the audience to the custom domain token endpoint.
26.2.0
This version contains only minor changes and improvements such as dependency updates.
26.1.18
This version contains only minor changes and improvements such as dependency updates.
26.1.17
This version contains only minor changes and improvements such as dependency updates.
26.1.16
This version contains only minor changes and improvements such as dependency updates.
26.1.15
This version contains only minor changes and improvements such as dependency updates.
26.1.14
This version contains only minor changes and improvements such as dependency updates.
26.1.13
This version contains only minor changes and improvements such as dependency updates.
26.1.12
This version contains only minor changes and improvements such as dependency updates.
v26.1.11
Breaking changes
Upgrading from <26.1.7 will break pagination tokens. To ensure smooth upgrade, you must upgrade to 26.1.7 first.
26.1.10
This version contains only minor changes and improvements such as dependency updates.
26.1.9
This version contains only minor changes and improvements such as dependency updates.
26.1.8
This version contains only minor changes and improvements such as dependency updates.
v26.1.7
Optimized SQL migrations for PostgreSQL
We have improved SQL migrations for PostgreSQL in Ory Hydra when upgrading from Open Source release v2.2.0 or later. This change reduces the need to acquire exclusive table locks, and removes some unnecessary other work. Exclusive table locks in PostgreSQL block all writes to that table, thus stalling any in-flight requests.
Automatic retries for serialization failures on PostgreSQL
Ory Hydra OEL now automatically retries transactions failing with serialization errors in PostgreSQL. In high-throughput scenarios, this decreases the client error rate substantially in certain circumstances.
26.1.6
This version contains only minor changes and improvements such as dependency updates.
26.1.5
This version contains only minor changes and improvements such as dependency updates.
26.1.4
This version contains only minor changes and improvements such as dependency updates.
26.1.3
This version contains only minor changes and improvements such as dependency updates.
26.1.2
This version contains only minor changes and improvements such as dependency updates.
26.1.1
This version contains only minor changes and improvements such as dependency updates.
26.1.0
This version contains only minor changes and improvements such as dependency updates.
25.4.12
This version contains only minor changes and improvements such as dependency updates.
25.4.11
This version contains only minor changes and improvements such as dependency updates.
v25.4.10
Remove the audience: audit field in some log entries
An experiment was made in the past to use logs for auditing, by having a certain field (i.e. key-value) present in the log entry,
e.g. in JSON: "audience": "audit".
However, this approach was never used everywhere (only in some places in Hydra) and it was decided to stop the experiment.
As a result, the "audience": "audit" field is removed from these log entries. The rest of these log entries is unchanged.
Logs not using this field are unchanged.
Example before:
{"audience":"audit","level":"info","msg":"something happened","service_name":"Ory Hydra","service_version":"master","time":"2025-12-19T09:55:39.647379+01:00"}
Example after:
{"level":"info","msg":"something happened","service_name":"Ory Hydra","service_version":"master","time":"2025-12-19T09:55:39.647379+01:00"}
Breaking changes
None.
If you relied on the presence of this field in the logs, we recommend you use events or OpenTelemetry traces instead, which carry much more information.
Stricter URI validation for client metadata
Ory Hydra now enforces stricter validation rules for certain client metadata URIs when creating, updating or patching clients.
These fields include policy_uri, tos_uri, client_uri, and logo_uri. The URIs must now be valid URLs with either http or
https schemes. This change enhances security and ensures compliance with OAuth 2.0 and OpenID Connect specifications.
v25.4.9
Optionally Discard Skipped Consents
When enabled, the new feature_flags.discard_skipped_consents configuration option instructs Hydra to not store the full consent
(SQL table hydra_oauth2_flow). Instead, only an identifier is stored for a short period of time to prevent reuse attacks. It is
possible to skip consents for trusted OAuth2 clients per default by setting skip_consent to true for the trusted clients.
Otherwise, only consents that were already granted will be skipped.
Limitations
When this feature is enabled, OAuth2 client front- and back-channel logouts will not work anymore.
Example
feature_flags:
discard_skipped_consents: true
v25.4.8
Hard to revert SQL migration
This release includes an SQL migration for Ory Hydra that drops multiple constraints. Due to this, reverting the migration is not very performant and may cause downtime. There should be no reason to revert this migration, as the schema itself is fully backwards compatible. This version should not be skipped during an upgrade. If a version revert is necessary, we discourage running the down migrations at all.
25.4.7
This version contains only minor changes and improvements such as dependency updates.
25.4.6
This version contains only minor changes and improvements such as dependency updates.
v25.4.5
Critical SQL Migrations
This release adds SQL migrations that should be executed separately. We highly discourage skipping this version, as it might cause downtime during future upgrades.
25.4.4
This version contains only minor changes and improvements such as dependency updates.
v25.4.3
Add feature flag to disable access token persistence for stateless JWT tokens
Introduces new feature flags strategies.jwt.stateless.enabled and strategies.jwt.stateless.claim_name for Hydra that skips
database writes for JWT access tokens. When enabled, access tokens are issued as self-contained JWTs with a top-level claim
indicating to be stateless, and operations that require token state (introspection, revocation, userinfo) return 501 Not
Implemented. This optimization significantly improves performance for workloads that use JWT access tokens and don't require token
revocation or introspection capabilities.
Internal Bugfixes
This release includes some internal bugfixes that are required to ensure zero-downtime upgrades of future versions. Therefore, it is highly recommended to not skip this version.
25.4.2
This version contains only minor changes and improvements such as dependency updates.
25.4.1
This version contains only minor changes and improvements such as dependency updates.
25.4.0
This version contains only minor changes and improvements such as dependency updates.
v25.3.9
Fix regression in refresh token UsedTimes calculation
Fixed a regression in Hydra OAuth2 refresh token handling where the UsedTimes validation was incorrectly checking the Valid
flag before comparing against the graceful count limit. This caused refresh tokens to be incorrectly rejected during the grace
period when the UsedTimes field was not explicitly set, even when the usage count was within acceptable limits.
The fix ensures that refresh tokens are properly validated based on the usage count alone, without requiring the UsedTimes field
to be explicitly marked as valid in the database.
25.3.8
This version contains only minor changes and improvements such as dependency updates.
25.3.7
This version contains only minor changes and improvements such as dependency updates.
25.3.6
This version contains only minor changes and improvements such as dependency updates.
v25.3.5
Change of base image
The base image for OEL images is now set to "gcr.io/distroless/static-debian12:nonroot". Previously, it was "gcr.io/distroless/static-debian12:debug-nonroot", which included BusyBox (a minimal shell and basic debugging utilities). Debug images are still available using the "-debug" tag suffix.
JWK Cache
Ory Hydra OEL now supports in-memory caching of JWKs to improve performance and reduce latency. The cache is enabled by setting
the db.jwk_cache.enabled configuration option to true. Other available configuration options are db.jwk_cache.size, which is
the maximum number of keys to be cached, and db.jwk_cache.ttl, which is the time-to-live for cached keys. It is recommended to
set a cache TTL to force regular refreshes of the keys. Updates to keys are not actively propagated to the cache, as clients are
expected to accept old keys for a certain period of time.
v25.3.4
Increased Refresh Token Grace Period
The maximum duration for the Refresh Token Rotation Grace Period has been extended from 30 days to 180 days in Ory Hydra with the Ory Enterprise License (OEL). For Ory Network projects and non-OEL Hydra, the maximum grace period remains 5 minutes.
The extended 180-day period is only available when a reuse count (rotation_grace_reuse_count) is configured. Without a reuse count, the limit remains 5 minutes.
Important: The 180-day grace period is considered insecure for general use. It is offered exclusively to support legacy migration scenarios and is not recommended for production deployments.
v25.3.3
Improved tracing and metrics for the high-performance SQL connection pool
This change adds distributed tracing and advanced metrics for the high-performance SQL connection pool in all Ory OEL products.
2025-06-11
Tag: 290abca8469dc46c1ba07708849fed28fdbc1b69
Add hydra debug commands
Changes:
- the hydra debug challenge
<challenge>command is available in the Ory Hydra Enterprise build and offers the ability to decode various consent challenge and verifiers. Use hydra debug challenge --help for detailed documentation. - the hydra debug encrypted-session
<session>command for Ory Hydra Enterprise offers the ability to decrypt encrypted session data which is kept in the database.
2025-05-09
Update OSS dependency of Hydra OEL
Changes:
- fix for sql migration for Azure Postgres Flexible Server - https://github.com/ory/hydra/pull/3958
- introduces implementation of OAuth 2.0 Device Authorization Grant - https://github.com/ory/hydra/pull/3912
- enables "high performance pooling" parameters in the DSN for PostgreSQL and CockroachDB
2025-03-19
Reduce memory usage of Ory Hydra OEL
This change addresses high memory consumption issue by correctly initializing the sessionCheckCache
2024-12-17
Handle concurrent refreshes and improve graceful refreshing
This patch improves Ory Hydra's ability to deal with refresh flows which, for example, concurrently refresh the same token. Furthermore, graceful token refresh has been improved to handle a variety of edge cases and scenarios.
This will remove/reduce serializable errors in CockroachDB during OAuth2 token exchanges.
https://www.ory.com/docs/hydra/guides/graceful-token-refresh
2024-07-17
Disable pre-configured multi-region config in Ory Hydra
Disables the pre-configured multi-region configuration for self-hosting Ory Enterprise License customers.