v26.3.4
v26.3.4
Account linking now works when registration is disabled
Automatic account linking now works when registration is disabled. Signing in with an OIDC or SAML provider whose email matches an
existing account starts the account linking flow even if selfservice.flows.registration.enabled is false. New sign-ups remain
blocked. Previously, the linking attempt failed with "Registration is not allowed because it was disabled."
Bound the password identifier-similarity check by length
The password validator's identifier-similarity check now bounds the length of the identifier and password it compares. Real identifiers and passwords are far shorter than this bound, so legitimate flows are unaffected. This hardens the check against excessive resource use on very long inputs.
Exclude already registered credentials in WebAuthn and passkey settings
The WebAuthn and passkey settings flows now populate excludeCredentials in the registration challenge with the identity's
already registered credentials. Browsers no longer silently overwrite an existing platform-authenticator entry (Touch ID, Windows
Hello, Android) when a user re-enrolls the same device; instead the authenticator reports the credential as already registered.
Kratos additionally rejects a registration response whose credential ID is already registered on the identity.
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.
Opt-in cleanup of expired data on CockroachDB
Ory Kratos Enterprise License deployments on CockroachDB can now set up row-level TTL jobs that automatically delete expired data.
Run the new kratos migrate row-ttl up command to configure these retention periods:
- Self-service flows, one-time codes, tokens, and continuity containers: 7 days after they expire.
- Sessions: 30 days after they expire.
- Courier messages, message dispatches, and self-service errors: 30 days after they are created.
The command is opt-in: kratos migrate sql up does not change TTL settings. Use kratos migrate row-ttl dump to print the SQL
statements — for review, or to adapt the retention periods and apply them manually. kratos migrate row-ttl status shows which
TTL migrations are applied, and kratos migrate row-ttl down removes the TTL configuration again.
Running kratos migrate row-ttl up replaces any custom row-level TTL settings on the affected tables with the defaults above. The
command requires CockroachDB and exits with an error on other databases.
CockroachDB deletes expired rows on a schedule, not at the exact moment the retention period ends. Expired rows can remain in the database for some time: either the TTL job has not processed them yet, or it has deleted them but garbage collection has not yet removed them.
Persist WebAuthn and passkey sign counter for clone detection
Ory now updates the stored WebAuthn and passkey authenticator state after every successful login. The signature counter, clone-warning flag, and backup state are written back to the credential, so W3C WebAuthn clone detection works as intended.
Previously the signature counter stayed at its registration value, which left clone detection inert. A login that presents a non-increasing counter is still allowed — synced passkeys (such as Apple iCloud or Google) legitimately report a static counter — but the possible-clone warning is now recorded on the credential and is visible through the admin identity API.
Revoke a single DeviceAuthn key by identifier
DELETE /admin/identities/{id}/credentials/deviceauthn now revokes a single DeviceAuthn key selected by the identifier query
parameter, where the identifier is the key's client_key_id. This is the same parameter that OIDC and SAML credential deletion
already use.
Because the client_key_id is unique per identity, administrators can revoke exactly one device key without affecting other keys
— including keys that share the same device name. Before, deleting the deviceauthn credential type removed all device keys at
once.
The endpoint returns 404 Not Found when the identity has no deviceauthn credential or when no key matches the identifier, and
400 Bad Request when the identifier is missing. Removing the last key leaves the deviceauthn credential with no keys.
Find the client key ids by calling GET /admin/identities/{id}?include_credential=deviceauthn.
Second-factor enrollment now refreshes the session's authenticated_at timestamp
Setting up a second factor — authenticator app (TOTP), security key, passkey, or lookup secret — in a settings flow now refreshes
the session's authenticated_at timestamp, in addition to elevating the session to AAL2. Linking a social sign-in provider in a
settings flow also refreshes the timestamp.
Previously, enrolling a second factor raised the session to AAL2 but left authenticated_at unchanged. Because AAL2
re-authentication prompts are timed from authenticated_at, users could be asked to re-authenticate immediately after proving
possession of the factor they had just enrolled. The timestamp now reflects the enrollment as the most recent authentication
event.
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.