- DATE:
- AUTHOR:
- Ory Team
Ory Network, Ory Hydra, Ory Kratos, Ory Keto, Ory Oathkeeper, Ory Polis, Ory Elements v26.4.9 released
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/rotateandDELETE /admin/clients/{id}/secrets/rotateenforce thehydra-admin-lowbucket, matching the other OAuth 2.0 client write operations.OPTIONS /admin/clients/{id}/secrets/rotateis rate limited by thehydra-admin-highbucket.POST /admin/test-login-flowsenforces thekratos-admin-lowbucket.DELETE /self-service/login/testenforces thekratos-public-highbucket.GET /.well-known/webauthnenforces thekratos-public-highbucket.
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/rotateandDELETE /admin/clients/{id}/secrets/rotateenforce thehydra-admin-lowbucket, matching the other OAuth 2.0 client write operations.OPTIONS /admin/clients/{id}/secrets/rotateis rate limited by thehydra-admin-highbucket.POST /admin/test-login-flowsenforces thekratos-admin-lowbucket.DELETE /self-service/login/testenforces thekratos-public-highbucket.GET /.well-known/webauthnenforces thekratos-public-highbucket.
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/dsyncno longer return acodefield insideerror. If your integration readserror.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, readerror_descriptioninstead.
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
apiControllerexport is removed. UseconnectionAPIController.The deprecated
config,getConfig,updateConfig, anddeleteConfigmethods are removed. UsecreateSAMLConnection,getConnections,updateSAMLConnection, anddeleteConnections.getConnectionsreturns an array of connections, not a single connection or an empty object.deleteConnectionsdeletes SAML and OIDC connections unless you passstrategy: 'saml'.The
GetConfigQueryandDelConfigQuerytypes are removed. UseGetConnectionsQueryandDelConnectionsQuery.
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
samlAudienceOverrideif it has one, and otherwiseSAML_AUDIENCE(samlAudiencein 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
samlAudienceOverrideon 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.