DATE:
AUTHOR:
Ory Team
Ory Network Ory Hydra Ory Kratos Ory Keto Ory Oathkeeper Ory Polis Ory Elements Ory Enterprise License

Ory Network, Ory Hydra, Ory Kratos, Ory Keto, Ory Oathkeeper, Ory Polis, Ory Elements v26.4.9 released

DATE:
AUTHOR: Ory Team

Ory Network

Choose which events an event stream delivers

An event stream can now be limited to a set of event types. Set event_types on the stream to the names of the events it should deliver, or to patterns such as Session* or *Failed in which * matches any characters, and the stream skips every other event. Leave the list empty to keep delivering every event, which is the behavior of all existing streams.

Set the filter when you create a stream, or change it on an existing stream with a partial update: omitting event_types keeps the current filter, and an empty list removes it. Unknown event names and patterns that match no event are rejected.

Throttle failed sign-in attempts per identifier and IP

Projects on an Enterprise plan can turn on brute-force protection in the Ory Console under Authentication > General, or with ory patch identity-config under selfservice.flows.login.brute_force_protection. It slows down repeated failed sign-in attempts per submitted identifier and per client IP, with a delay that doubles after each further failure up to a maximum you choose. Accounts are never locked.


This is now available on Ory Network.


Ory Hydra

Cleaner SBOM and CBOM attestations for OEL images

The SBOM attestations of OEL images now list the license of each Go module. They no longer list the timezone database, shell profile and license text files of the base image, the source repository metadata of the scanner, or secret-pattern matches that are not cryptographic assets. Each document is validated against its schema before it is signed. Tools that ingest the attestations need no changes.

Compromised eMudhra roots removed from the trust store and container images

The root certificates compiled into Ory Hydra, Ory Kratos, Ory Keto, and Ory Talos no longer include "emSign ECC Root CA - G3" and "Comodo AAA Services root", the roots distrusted after the eMudhra intermediate CA compromise. The binaries verify outbound TLS connections against this compiled-in root set, so this is the change that removes trust in those roots.

The Ory Enterprise License and open source container images are now also built on gcr.io/distroless/static-debian13, whose ca-certificates 20250419+deb13u1 package no longer ships either root (Debian advisory DSA-6572-1).

Rate limits now apply to client secret rotation and related endpoints

The following Ory Network API endpoints now enforce the rate limit bucket documented in their API specification:

  • POST /admin/clients/{id}/secrets/rotate and DELETE /admin/clients/{id}/secrets/rotate enforce the hydra-admin-low bucket, matching the other OAuth 2.0 client write operations.

  • OPTIONS /admin/clients/{id}/secrets/rotate is rate limited by the hydra-admin-high bucket.

  • POST /admin/test-login-flows enforces the kratos-admin-low bucket.

  • DELETE /self-service/login/test enforces the kratos-public-high bucket.

  • GET /.well-known/webauthn enforces the kratos-public-high bucket.

In addition, GET /.well-known/ory/webauthn.js moves from the hydra-public-high bucket to the more permissive kratos-public-high bucket, matching the other Ory Identities public endpoints on the login path.

If your automation calls these endpoints at a high rate, it may now receive 429 Too Many Requests responses. The limits for each bucket depend on your plan; see the rate limit documentation for the current values.

Reject authorization requests without PKCE before login when PKCE is required

When PKCE is required for a client, the authorization endpoint now rejects a request for an authorization code that has no code_challenge right away, with the invalid_request error. Before, the user first went through login and consent, and the request failed afterwards.

The discovery documents (/.well-known/openid-configuration and /.well-known/oauth-authorization-server) now list only S256 in code_challenge_methods_supported. They listed plain before, but the authorization endpoint never accepted it.


This is now available on Ory Network, for the Ory Enterprise License, and will be part of the next Ory Open Source release.


Ory Kratos

Cleaner SBOM and CBOM attestations for OEL images

The SBOM attestations of OEL images now list the license of each Go module. They no longer list the timezone database, shell profile and license text files of the base image, the source repository metadata of the scanner, or secret-pattern matches that are not cryptographic assets. Each document is validated against its schema before it is signed. Tools that ingest the attestations need no changes.

Code emails let email clients autofill the code

Login, registration, recovery, and verification code emails sent over SMTP now include an OTP-Token header, as proposed in draft-goto-otp-token. The header carries the code and the origin of the site that requested it, for example OTP-Token: "123456"; origin="https://auth.example.com". Email clients that support the header can offer to fill in the code on that site. Emails sent through the HTTP delivery strategy are unchanged.

Compromised eMudhra roots removed from the trust store and container images

The root certificates compiled into Ory Hydra, Ory Kratos, Ory Keto, and Ory Talos no longer include "emSign ECC Root CA - G3" and "Comodo AAA Services root", the roots distrusted after the eMudhra intermediate CA compromise. The binaries verify outbound TLS connections against this compiled-in root set, so this is the change that removes trust in those roots.

The Ory Enterprise License and open source container images are now also built on gcr.io/distroless/static-debian13, whose ca-certificates 20250419+deb13u1 package no longer ships either root (Debian advisory DSA-6572-1).

Failed emails and SMS are retried over a longer period

When the Ory Network cannot deliver an email or SMS, it now waits longer before each retry, instead of retrying within seconds. This gives your SMTP server or HTTP delivery endpoint time to recover from a short outage before the message is abandoned.

The number of retries, set by courier.message_retries, does not change.

Rate limits now apply to client secret rotation and related endpoints

The following Ory Network API endpoints now enforce the rate limit bucket documented in their API specification:

  • POST /admin/clients/{id}/secrets/rotate and DELETE /admin/clients/{id}/secrets/rotate enforce the hydra-admin-low bucket, matching the other OAuth 2.0 client write operations.

  • OPTIONS /admin/clients/{id}/secrets/rotate is rate limited by the hydra-admin-high bucket.

  • POST /admin/test-login-flows enforces the kratos-admin-low bucket.

  • DELETE /self-service/login/test enforces the kratos-public-high bucket.

  • GET /.well-known/webauthn enforces the kratos-public-high bucket.

In addition, GET /.well-known/ory/webauthn.js moves from the hydra-public-high bucket to the more permissive kratos-public-high bucket, matching the other Ory Identities public endpoints on the login path.

If your automation calls these endpoints at a high rate, it may now receive 429 Too Many Requests responses. The limits for each bucket depend on your plan; see the rate limit documentation for the current values.

Resent emails keep their Message-ID

Every email that Ory Kratos Identities sends over SMTP now carries a Message-ID header built from the message ID and the domain of courier.smtp.from_address. When an email is sent again, for example after a delivery whose outcome could not be recorded, it carries the same Message-ID. Mailbox providers can then recognize the copy, so recipients usually don't receive the email twice.

Ory Kratos Identities ignores a Message-ID set in courier.smtp.headers, because a configured value would be the same for every email.

Run a web hook when a login attempt fails

The new selfservice.flows.login.failure.hooks trigger calls a web hook in the background each time Ory Kratos Identities rejects a login attempt's credentials, for example a wrong password or a rejected social sign-in. Use it to report failed logins to a fraud detection service. The web hook gets ctx.failure with the authentication method, the submitted identifier, and the matched identity ID, together with the request's transient_payload. It can't change the response to the user.

SCIM group writes return a conflict when another request is changing the group

PUT /Groups/{id} and PATCH /Groups/{id} now return 409 Conflict right away when another request is changing the same group. Before, the request waited for the other one to finish and often failed with 500 Internal Server Error after up to 25 seconds.

They also return 409 Conflict when another request changed the group after this one read it. Before, the later request could succeed and silently undo the other request's change, for example removing a member that the other request had just added.

The 409 response carries a Retry-After header. Retry the request after that many seconds. To avoid the error, send writes to one group one at a time.

Throttle failed sign-in attempts per identifier and IP

Projects on an Enterprise plan can turn on brute-force protection in the Ory Console under Authentication > General, or with ory patch identity-config under selfservice.flows.login.brute_force_protection. It slows down repeated failed sign-in attempts per submitted identifier and per client IP, with a delay that doubles after each further failure up to a maximum you choose. Accounts are never locked.


This is now available on Ory Network, for the Ory Enterprise License, and will be part of the next Ory Open Source release.


Ory Keto

Cleaner SBOM and CBOM attestations for OEL images

The SBOM attestations of OEL images now list the license of each Go module. They no longer list the timezone database, shell profile and license text files of the base image, the source repository metadata of the scanner, or secret-pattern matches that are not cryptographic assets. Each document is validated against its schema before it is signed. Tools that ingest the attestations need no changes.

Compromised eMudhra roots removed from the trust store and container images

The root certificates compiled into Ory Hydra, Ory Kratos, Ory Keto, and Ory Talos no longer include "emSign ECC Root CA - G3" and "Comodo AAA Services root", the roots distrusted after the eMudhra intermediate CA compromise. The binaries verify outbound TLS connections against this compiled-in root set, so this is the change that removes trust in those roots.

The Ory Enterprise License and open source container images are now also built on gcr.io/distroless/static-debian13, whose ca-certificates 20250419+deb13u1 package no longer ships either root (Debian advisory DSA-6572-1).


This is now available for the Ory Enterprise License and will be part of the next Ory Open Source release.


Ory Oathkeeper

Cleaner SBOM and CBOM attestations for OEL images

The SBOM attestations of OEL images now list the license of each Go module. They no longer list the timezone database, shell profile and license text files of the base image, the source repository metadata of the scanner, or secret-pattern matches that are not cryptographic assets. Each document is validated against its schema before it is signed. Tools that ingest the attestations need no changes.

Compromised eMudhra roots removed from the trust store and container images

The root certificates compiled into Ory Hydra, Ory Kratos, Ory Keto, and Ory Talos no longer include "emSign ECC Root CA - G3" and "Comodo AAA Services root", the roots distrusted after the eMudhra intermediate CA compromise. The binaries verify outbound TLS connections against this compiled-in root set, so this is the change that removes trust in those roots.

The Ory Enterprise License and open source container images are now also built on gcr.io/distroless/static-debian13, whose ca-certificates 20250419+deb13u1 package no longer ships either root (Debian advisory DSA-6572-1).


This is now available for the Ory Enterprise License and will be part of the next Ory Open Source release.


Ory Polis

PKCE now requires the S256 code challenge method

By default, the OAuth authorization endpoint (/api/oauth/authorize) accepts only the S256 code challenge method. A request that sends a code_challenge with code_challenge_method=plain, with any other value, or without the parameter at all is rejected with an invalid_request error returned to the redirect URI. The code_challenge_methods_supported field of the OpenID configuration document lists S256 alone, and includes plain on deployments that enable it.

Behavior changes

Clients that use plain, or that rely on the OAuth default of plain by omitting code_challenge_method, stop working. Move them to S256, where the challenge is the SHA-256 hash of the code verifier, base64url encoded without padding, sent together with code_challenge_method=S256. Most OAuth libraries do this for you.

The method value is matched exactly, as RFC 7636 requires, so s256 and other spellings are rejected as well. A request that sends an empty code_challenge, or that repeats the code_challenge or code_challenge_method parameter, is refused as malformed: an empty challenge is not the same as a request without PKCE. Those two parameters are checked before the redirect URI has been validated, so the error goes back to the caller rather than to the redirect URI.

If a client cannot be changed yet, set OPENID_PKCE_ALLOW_PLAIN=true (npm library option openid.pkceAllowPlain) to keep accepting plain while you migrate. plain carries the code verifier in the authorization request in clear text, so remove the setting once every client uses S256. Requests that name a method Ory Polis does not implement are rejected either way.

Polis API errors share one format

Every management API route now reports an error as { "error": { "message": "..." } }, with the HTTP status carrying the error code. For a server error, the message no longer includes internal details.

A request that uses a method an endpoint does not support now gets a 405 response with an Allow header listing the supported methods. The endpoints a browser reaches during login and logout, such as the SAML ACS URL and /api/logout, send the browser to the Ory Polis error page with that 405 instead.

Breaking changes

  • The directory sync routes under /api/v1/dsync no longer return a code field inside error. If your integration reads error.code, read the HTTP status instead.

  • DELETE /api/v1/dsync/{directoryId} reports a failed delete. Deleting a directory that does not exist returns 404 instead of 200.

  • The OAuth token endpoint (/api/oauth/token) answers a failed request with the JSON body RFC 6749 defines, { "error": "...", "error_description": "..." }, instead of a plain-text message. The HTTP status codes are unchanged. OAuth client libraries read this format already. If you parse the plain-text body yourself, read error_description instead.

Removed deprecated SSO connection APIs and legacy API paths

Ory Polis no longer serves the deprecated SAML configuration API or the old API paths kept for backward compatibility.

Breaking changes

These HTTP routes are removed and now return 404 Not Found:

Removed routeUse instead/api/v1/saml/config/api/v1/sso/api/v1/saml/config/exists/api/v1/sso/exists/api/v1/connections/*/api/v1/sso/*/api/v1/directory-sync/*/api/v1/dsync/*/api/v1/saml-traces/*/api/v1/sso-traces/*/api/v1/federated-saml/*/api/v1/identity-federation/*

If you embed Polis with the npm package (@boxyhq/saml-jackson):

  • The apiController export is removed. Use connectionAPIController.

  • The deprecated config, getConfig, updateConfig, and deleteConfig methods are removed. Use createSAMLConnection, getConnections, updateSAMLConnection, and deleteConnections. getConnections returns an array of connections, not a single connection or an empty object. deleteConnections deletes SAML and OIDC connections unless you pass strategy: 'saml'.

  • The GetConfigQuery and DelConfigQuery types are removed. Use GetConnectionsQuery and DelConnectionsQuery.

Update any scripts, integrations, or code that use these routes or methods before you upgrade.

SAML audiences must match the SP entity ID exactly

Polis now accepts a SAML response only when its Audience is exactly one of the connection's two SP entity IDs:

  • the SP entity ID: the connection's samlAudienceOverride if it has one, and otherwise SAML_AUDIENCE (samlAudience in the npm library)

  • the SP entity ID followed by / and the tenant and product identifier, which the SSO connection setup page shows for identity providers that need a unique entity ID per app

Both keep working. Before, Polis also accepted any Audience that only started with the SP entity ID, such as https://saml.boxyhq.com/ with a trailing slash, or the entity ID shown for a different tenant or product. Logins through a SAML connection whose identity provider sends such a value now fail with Invalid audience.

The setup page builds the entity ID it shows from SAML_AUDIENCE, even for a connection with samlAudienceOverride. For such a connection, the second SP entity ID is the override followed by the same / and identifier, so the value the setup page shows is not accepted. Use the override, or replace SAML_AUDIENCE at the start of the value the setup page shows with the override.

What you must do

Before you upgrade, compare the Entity ID, Audience URI, or Audience Restriction in each customer's SAML app with the connection's two SP entity IDs. It must be identical to one of them, including case and any trailing slash. For each connection that differs, do one of the following:

  • Have the customer set the Entity ID in their SAML app to one of the two SP entity IDs.

  • If the identity provider can't be changed, set samlAudienceOverride on the connection to the value the identity provider sends. Polis then also uses that value as the issuer of the SAML authentication requests it sends to that identity provider.

After you upgrade, failed logins appear in SSO Traces with the error Invalid audience. The upgrade guide has the same steps.

Cleaner SBOM and CBOM attestations for OEL images

The SBOM attestations of OEL images now list the license of each Go module. They no longer list the timezone database, shell profile and license text files of the base image, the source repository metadata of the scanner, or secret-pattern matches that are not cryptographic assets. Each document is validated against its schema before it is signed. Tools that ingest the attestations need no changes.

Polis rejects tenant and product selectors that conflict with the client ID

An authorization request that names a registered client, either an SSO connection's client ID or an identity federation app, now only signs in through connections that client is allowed to use. If the request also passes tenant and product, or encodes them in access_type, resource or scope, they must match the client's connection or fall within the federation app's tenants. Otherwise Polis rejects the request.

Selecting a connection by tenant and product works as before when client_id is dummy, the encoded tenant=<tenant>&product=<product> value, or a value that is not a registered client ID.

SCIM POST /Groups now applies group members

Creating a group with a members list now persists that membership. Until now the create returned 201 and kept the group's name, but discarded the members, so any client that provisions membership on create left the group empty. A create without members still creates an empty group, and a malformed members list is now rejected with 400 and the scimType invalidValue instead of failing with a server error. If applying the members fails, the create is undone and fails, so the identity provider can retry it.


This is now available for the Ory Enterprise License and will be part of the next Ory Open Source release.


Ory Elements

@ory/nextjs resolves the Ory URL the same way on the server and in the browser

The flow functions in @ory/nextjs/app and getError now attach the URL the browser uses to reach Ory to what they return. Ory Elements components read it from there, so the server and the browser always use the same URL, including on preview deployments. SessionProvider fetches the session itself: pass it baseUrl={await getOryBaseUrl()}.

When that URL can't work, for example because the app and Ory are on different sites and the middleware didn't handle the page, @ory/nextjs logs a warning with the fix once in the server log.

Breaking changes

trustedOrigins in createOryMiddleware accepts only strings. Replace each regular expression with a glob, where * matches one or more letters, digits, or hyphens within one part of the host name. For example, replace /^https:\/\/my-app-[a-z0-9-]+-my-team\.vercel\.app$/ with "https://my-app-*-my-team.vercel.app". A * can't make up the whole label right before a public suffix, so https://*.vercel.app is rejected. createOryMiddleware throws at startup when an entry has a path, has no scheme, or is otherwise invalid.

The middleware is for local development and preview deployments. Remove your production origin from trustedOrigins and use a custom domain in production.

Fix Ory Elements staying disabled after canceling Sign in with Apple

On iOS, iPadOS, and Safari on macOS, Sign in with Apple opens Apple's native sign-in sheet. Canceling the sheet no longer leaves the Ory Elements login form disabled with a loading spinner until the page is reloaded. The form is usable again right after the sheet closes.


This is now available on Ory Network, for the Ory Enterprise License, and will be part of the next Ory Open Source release.


Ory Terraform

No significant changes in this release.

Powered by LaunchNotes