Skip to main content

v26.2.10

v26.2.10

Add unified bulk session management endpoint

The admin session API now exposes a single endpoint for bulk disable and bulk delete operations across multiple identities or sessions:

  • POST /admin/sessions (manageSessions)

The action field selects the operation:

  • action: "disable" — soft-revoke matching sessions (sets active = false, preserves audit data).
  • action: "delete" — permanently delete matching sessions.

Targets are filtered by identities (a list of identity IDs) or sessions (a list of session IDs); exactly one of the two must be provided. To scope the operation to every session in the network, pass identities: ["*"]; the wildcard is not accepted in the sessions field and may not be mixed with explicit IDs. Up to 500 explicit IDs are accepted per call.

All requests respond 200 OK with {"processed": N, "more": <bool>}. processed reports how many rows the call affected (for disable, only sessions that were active before the call). more is true only when a wildcard request reached the per-call batch limit and additional matching rows may remain; callers should re-issue the same request to continue draining. Explicit-IDs requests always return more: false.

client.manageSessions({ action: "disable", identities: ["uuid-a", "uuid-b"] })
client.manageSessions({ action: "delete", sessions: ["uuid-c"] })
client.manageSessions({ action: "disable", identities: ["*"] })

No existing endpoints are changed.

Fix admin identity import for TOTP, lookup-secret, and passkey credentials

This change fixes two related issues with the admin identity import API (POST /admin/identities and PUT /admin/identities/{id}).

TOTP and lookup-secret can now be used at AAL2 login after import. Previously, the import wrote the credential row but did not write the matching row in identity_credential_identifiers, so the AAL2 login flow could not resolve the credential and returned "You have no TOTP device set up." (4000015) for TOTP, or "You have not configured backup codes yet." for lookup-secret.

Passkey imports without a user_handle are now rejected with a 400. A passkey's identifier is the user_handle returned by the authenticator during the WebAuthn assertion. The userHandle is generated at registration time and stored on the authenticator, so it cannot be reconstructed by the server: the import has to supply it, or inherit it from a previously persisted passkey credential. Previously, an import that omitted user_handle would silently persist a non-functional credential. The import now rejects this case up front, and also rejects imports that supply an empty credentials.passkey.config.credentials list.

Reject imported password hashes with extreme cost parameters

Kratos now bounds the cost parameters embedded in imported password hashes. Hashes that declare cost parameters far above any sane production value are rejected at the admin identity import API with a 400 Bad Request, and at login time the comparator returns a clear error instead of allocating gigabytes of memory or running for hours.

This closes a denial-of-service class where an attacker with write access to the identity graph could persist a hash whose decoded parameters would crash the Kratos process on every subsequent login attempt for that identity.

The bounds apply to Argon2 (m, t, p), PBKDF2 (i), plain scrypt (N, r, p), Firebase scrypt (ln, r, p), and bcrypt (cost). They are set comfortably above strong real-world configurations: Argon2 memory up to 1 GiB, PBKDF2 iterations up to 10,000,000, scrypt N up to 2^20, Firebase scrypt ln up to 17, and bcrypt cost up to 17.

Imports of correctly-configured hashes are unaffected.