Skip to main content

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_total counter attaches an exemplar whose value is the transaction's retry count when a transaction is retried during a traced request, and the trace records a db.transaction.retry span 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.