v26.3.15
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.
Sensitive values are now encrypted by default
Kratos used to default to the no-op cipher, which stored sensitive values in plaintext — OIDC provider tokens, and DeviceAuthn PIN
secrets in Ory Enterprise License deployments. ciphers.algorithm now defaults to [xchacha20-poly1305, noop]: new values are
encrypted with XChaCha20 Poly1305, and values already stored in plaintext keep decrypting until they are written again. An unknown
algorithm no longer falls back to plaintext; it fails closed, and every encrypt or decrypt returns an error.
Breaking changes
secrets.cipher is now required. Kratos refuses to start unless it holds at least one 32-character secret. Add it to your
configuration before you upgrade — the current version accepts the setting and ignores it, so the rollout is:
- Add a 32-character
secrets.cipher. - Roll out this version.
To keep storing these values in plaintext, set ciphers.algorithm: noop explicitly. This provides no confidentiality or integrity
and is not recommended. secrets.cipher is required either way.
During the rolling upgrade, instances still running the previous version can't read values that upgraded instances have already
encrypted. If you can't tolerate that window, set ciphers.algorithm: noop explicitly in step 1 and remove it once every instance
runs the new version. Both instances then agree on the algorithm throughout the rollout, and the switch happens on a configuration
change rather than across the deployment.
Changing the algorithm
ciphers.algorithm also accepts an ordered list. The first algorithm encrypts new values; the rest only decrypt older ones, and
each value is re-encrypted with the first algorithm on its next write. This mirrors secrets.cipher key rotation, one level up,
so you can change algorithms without re-encrypting existing data and without downtime. noop is only valid as the last entry,
because it decodes any value and would shadow the algorithms after it.
Import identities faster with all-or-nothing batches
PATCH /admin/identities accepts a new with_partial_inserts field in the request body. It defaults to true, which is the
existing behavior: each identity is imported on its own, and an identity that conflicts with one that already exists is reported
individually while the rest of the batch is still imported.
Set it to false to import the batch as a single unit. The whole batch is then written in one transaction, which is considerably
faster. In a cluster whose regions are 250 ms apart, a batch of 10 identities that took 2.2 seconds takes 0.4 seconds, and a batch
of 1000 that took 7 seconds takes 2.5. In exchange, a single conflict fails the request with 409 Conflict and no identity from
the batch is created.
Use it when you expect the batch to contain no duplicates. That is the case when you import from a database that already enforces
the same unique constraints, or when you have validated the data beforehand. You can also use it optimistically: send the batch
with with_partial_inserts set to false, and repeat the request with true if it returns 409 Conflict.
The response does not say which identities conflicted, because none of them were imported. The retry with with_partial_inserts
set to true imports every identity that does not conflict and reports a per-identity error for the ones that do.
Support IdP-initiated SAML login
Enterprise users can now start a login from their identity provider's app launcher (for example the Okta or Azure AD dashboard) and land in your application with a valid Ory session.
When an identity provider posts an unsolicited SAML response, Ory Polis validates it and redirects the browser to a new Kratos
entry point with a single-use authorization code. Kratos starts a regular SP-initiated login flow — with the usual state, CSRF,
and PKCE protections — and forwards the code as a code_hint to the Polis authorize endpoint. Polis consumes the hint and
completes the flow immediately, without a second round-trip to the identity provider.
Standalone Ory Polis already supports IdP-initiated SAML login through its direct redemption flow. This change adds the Kratos
side: an entry point that folds the unsolicited response into a regular SP-initiated Kratos login, wired together with Polis
through the code_hint.
To enable it on Ory Network, turn on the "IdP-initiated login" toggle in the SAML provider settings in Ory Console. Ory then points the SAML connection's default redirect URL at the entry point automatically.
To enable it on a self-hosted Ory deployment that runs Kratos and Polis together:
- Set
idp_initiated_login_enabled: trueon the SAML (jackson) provider in your Kratos configuration. - Point the Polis SAML connection's
defaultRedirectUrlathttps://<your-project>/self-service/methods/saml/idp-initiated/<provider>. - Run Polis with
IDP_ENABLED=true.
IDP_ENABLED is the deployment-wide switch. A connection follows it unless it sets idpInitiatedLoginEnabled explicitly: true
always allows IdP-initiated login for that connection and false always blocks it. On Ory Network the switch stays off and the
Console toggle sets the per-connection flag, so no deployment-wide change is needed.
The code hint is single use, expires with the code store TTL, and is only valid for the exact connection it was issued for. SP-initiated codes cannot be used as hints. If a hint is invalid or already consumed, the user is sent through the regular SP-initiated redirect instead — usually completing silently against the identity provider's existing session.
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.