Skip to content

2.4.0-rc.6

Pre-release
Pre-release

Choose a tag to compare

@lakhansamani lakhansamani released this 22 Jul 12:31
baf08fe

Security

Critical: signup could bypass MFA enforcement entirely. SignUpRequest.is_multi_factor_auth_enabled — a field intended only for the authenticated admin _update_user override — was also honored on the public, unauthenticated signup mutation/RPC, letting a caller decide whether MFA applied to the account they were creating. This was confirmed live and reproducible in 2.4.0-rc.0 through rc.5:

  • REST (/v1/signup) and gRPC (Signup) were affected on every signup, unconditionally, with no attacker action needed. The proto field was a bare bool (not optional bool), so it always carried a value on the wire (defaulting to false); the gRPC handler forwarded that value straight through, silently disabling MFA for every account created via REST or gRPC regardless of server config.
  • GraphQL was exploitable by any caller explicitly passing is_multi_factor_auth_enabled: false.
  • The flag persisted on the created user row, so every subsequent login for an affected account skipped MFA too, not just the initial signup response.

Fix, in two parts:

  1. SignUp() no longer honors this field at all — new accounts always defer to the server's own --enable-*-mfa/--enforce-mfa config.
  2. Breaking change: the field is removed entirely from SignUpRequest in the GraphQL schema and proto (field 15 reserved, not reused) — the bypass can no longer even be expressed in a request, on any transport. REST is grpc-gateway-generated from the same proto, so this one change covers GraphQL, REST, and gRPC together.

Nothing legitimate depended on this: the built-in login UI (web/app) never sent this field at signup. Its one real use — the authenticated admin _update_user path (web/dashboard's Users page) toggling MFA for an existing user — is untouched. The existing skip_mfa_setup mutation remains the correct, safely-guarded way for a user to decline an offered MFA setup (requires proof of the first factor via the MFA session cookie, and refuses when --enforce-mfa is set).

Action required: any client (custom integration, SDK, or hand-rolled REST/gRPC call) that sets is_multi_factor_auth_enabled on signup must remove it — the field no longer exists on the wire. If you rely on a generated client from the proto/GraphQL schema, regenerate it against this version.

Verified via make test (SQLite), make test-all-db (all 7 backing databases), make smoke (GraphQL + REST + gRPC + MCP end-to-end), and make lint/make proto-lint — all clean.

This is a release candidate (pre-release). Run make test-all-db cross-DB validation before promoting to a stable release.