[GH-ISSUE #7212] send-email-login rejects bw CLI requests with 422 (deviceIdentifier required but unused in email+password branch) #30137

Closed
opened 2026-06-17 09:52:58 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @hoobio on GitHub (May 13, 2026).
Original GitHub issue: https://github.com/dani-garcia/vaultwarden/issues/7212

Prerequisites

Vaultwarden Support String

Your environment (Generated via diagnostics page)

  • Vaultwarden version: v1.36.0
  • Web-vault version: v2026.4.1
  • OS/Arch: linux/x86_64
  • Running within a container: true (Base: Debian)
  • Database type: SQLite
  • Database version: 3.51.3
  • Uses config.json: false
  • Uses a reverse proxy: true
  • IP Header check: true (CF-Connecting-IP)
  • Internet access: true
  • Internet access via a proxy: false
  • DNS Check: true
  • TZ environment: Australia/Melbourne
  • Browser/Server Time Check: true
  • Server/NTP Time Check: true
  • Domain Configuration Check: true
  • HTTPS Check: true
  • Websocket Check: false
  • HTTP Response Checks: true

Config & Details (Generated via diagnostics page)

Show Config & Details

Config:

{
  "_duo_akey": null,
  "_enable_duo": true,
  "_enable_email_2fa": true,
  "_enable_smtp": true,
  "_enable_yubico": true,
  "_icon_service_csp": "",
  "_icon_service_url": "",
  "_ip_header_enabled": true,
  "_max_note_size": 10000,
  "_smtp_img_src": "***:",
  "admin_ratelimit_max_burst": 3,
  "admin_ratelimit_seconds": 300,
  "admin_session_lifetime": 20,
  "admin_token": "***",
  "allowed_connect_src": "",
  "allowed_iframe_ancestors": "",
  "attachments_folder": "data/attachments",
  "auth_request_purge_schedule": "30 * * * * *",
  "authenticator_disable_time_drift": false,
  "data_folder": "data",
  "database_conn_init": "",
  "database_idle_timeout": 600,
  "database_max_conns": 10,
  "database_min_conns": 2,
  "database_timeout": 30,
  "database_url": "***************",
  "db_connection_retries": 15,
  "disable_2fa_remember": false,
  "disable_admin_token": true,
  "disable_icon_download": false,
  "dns_prefer_ipv6": false,
  "domain": "*****://**************",
  "domain_origin": "*****://**************",
  "domain_path": "",
  "domain_set": true,
  "duo_context_purge_schedule": "30 * * * * *",
  "duo_host": null,
  "duo_ikey": null,
  "duo_skey": null,
  "duo_use_iframe": false,
  "email_2fa_auto_fallback": false,
  "email_2fa_enforce_on_verified_invite": false,
  "email_attempts_limit": 3,
  "email_change_allowed": true,
  "email_expiration_time": 600,
  "email_token_size": 6,
  "emergency_access_allowed": true,
  "emergency_notification_reminder_schedule": "0 3 * * * *",
  "emergency_request_timeout_schedule": "0 7 * * * *",
  "enable_db_wal": true,
  "enable_websocket": true,
  "enforce_single_org_with_reset_pw_policy": false,
  "event_cleanup_schedule": "0 10 0 * * *",
  "events_days_retain": null,
  "experimental_client_feature_flags": "",
  "extended_logging": true,
  "helo_name": null,
  "hibp_api_key": null,
  "http_request_block_non_global_ips": true,
  "http_request_block_regex": null,
  "icon_blacklist_non_global_ips": true,
  "icon_blacklist_regex": null,
  "icon_cache_folder": "data/icon_cache",
  "icon_cache_negttl": 259200,
  "icon_cache_ttl": 2592000,
  "icon_download_timeout": 10,
  "icon_redirect_code": 302,
  "icon_service": "internal",
  "incomplete_2fa_schedule": "30 * * * * *",
  "incomplete_2fa_time_limit": 3,
  "increase_note_size_limit": false,
  "invitation_expiration_hours": 120,
  "invitation_org_name": "Vaultwarden",
  "invitations_allowed": true,
  "ip_header": "CF-Connecting-IP",
  "job_poll_interval_ms": 30000,
  "log_file": "/data/vaultwarden.log",
  "log_level": "info",
  "log_timestamp_format": "%Y-%m-%d %H:%M:%S.%3f",
  "login_ratelimit_max_burst": 10,
  "login_ratelimit_seconds": 60,
  "org_attachment_limit": null,
  "org_creation_users": "",
  "org_events_enabled": false,
  "org_groups_enabled": false,
  "password_hints_allowed": true,
  "password_iterations": 600000,
  "purge_incomplete_sso_auth": "0 20 0 * * *",
  "push_enabled": false,
  "push_identity_uri": "https://identity.bitwarden.com",
  "push_installation_id": "***",
  "push_installation_key": "***",
  "push_relay_uri": "https://push.bitwarden.com",
  "reload_templates": false,
  "require_device_email": false,
  "rsa_key_filename": "data/rsa_key",
  "send_purge_schedule": "0 5 * * * *",
  "sendmail_command": null,
  "sends_allowed": true,
  "sends_folder": "data/sends",
  "show_password_hint": false,
  "signups_allowed": false,
  "signups_domains_whitelist": "",
  "signups_verify": false,
  "signups_verify_resend_limit": 6,
  "signups_verify_resend_time": 3600,
  "smtp_accept_invalid_certs": false,
  "smtp_accept_invalid_hostnames": false,
  "smtp_auth_mechanism": "Login",
  "smtp_debug": false,
  "smtp_embed_images": true,
  "smtp_explicit_tls": null,
  "smtp_from": "**************",
  "smtp_from_name": "*****",
  "smtp_host": "******************",
  "smtp_password": "***",
  "smtp_port": 587,
  "smtp_security": "starttls",
  "smtp_ssl": null,
  "smtp_timeout": 15,
  "smtp_username": "*************",
  "sso_allow_unknown_email_verification": false,
  "sso_audience_trusted": null,
  "sso_auth_only_not_session": false,
  "sso_authority": "",
  "sso_authorize_extra_params": "",
  "sso_callback_path": "*****://*******************************************",
  "sso_client_cache_expiration": 0,
  "sso_client_id": "",
  "sso_client_secret": "***",
  "sso_debug_tokens": false,
  "sso_enabled": false,
  "sso_master_password_policy": null,
  "sso_only": false,
  "sso_pkce": true,
  "sso_scopes": "email profile",
  "sso_signups_match_email": true,
  "templates_folder": "data/templates",
  "tmp_folder": "data/tmp",
  "trash_auto_delete_days": null,
  "trash_purge_schedule": "0 5 0 * * *",
  "use_sendmail": false,
  "use_syslog": false,
  "user_attachment_limit": null,
  "user_send_limit": null,
  "web_vault_enabled": true,
  "web_vault_folder": "web-vault/",
  "yubico_client_id": null,
  "yubico_secret_key": null,
  "yubico_server": null
}

Vaultwarden Build Version

v1.36.0

Deployment method

Official Container Image

Custom deployment method

n/a

Reverse Proxy

nginx (in front of Cloudflare Tunnel) + OWASP ModSecurity CRS sidecar in detection mode. Verified the WAF is not the cause: the 422 is generated by vaultwarden's data guard before the handler runs.

Host/Server Operating System

Linux

Operating System Version

Debian GNU/Linux 13 (trixie), kernel 6.17.13, x86_64.

Clients

  • CLI

Client Version

Bitwarden CLI v2026.4.1 (Web-Vault 2026.4.1)

Steps To Reproduce

  1. Configure email 2FA on a vaultwarden account.
  2. Run bw login <email> --passwordenv BW_MP --method 1 (no --code) against vaultwarden, with BW_MP set to the master password.
  3. CLI authenticates with the master password (POST /identity/connect/token returns 400 with 2FA token not provided, as expected).
  4. CLI calls POST /api/two-factor/send-email-login to trigger the code email.

Expected Result

vaultwarden sends the email code. The CLI then prompts for the token.

Actual Result

vaultwarden returns 422. No code email is ever sent. Roughly three minutes later the user receives the standard incomplete-2FA security warning email instead.

Logs

[INFO] POST /api/two-factor/send-email-login
[WARN] Data guard `Json < SendEmailLoginData >` failed:
  Parse("{\"email\":\"alex@example.com\",\"masterPasswordHash\":\"...\"}",
        Error("missing field `deviceIdentifier`", line: 1, column: 93)).
[INFO] (send_email_login) POST /api/two-factor/send-email-login => 422 Unprocessable Entity

Screenshots or Videos

n/a

Additional Context

Root cause

SendEmailLoginData declares device_identifier: DeviceId (mandatory):

struct SendEmailLoginData {
    #[serde(alias = "DeviceIdentifier")]
    device_identifier: DeviceId,
    email: Option<String>,
    master_password_hash: Option<String>,
    auth_request_id: Option<AuthRequestId>,
    auth_request_access_code: Option<String>,
}

But device_identifier is only used in the SSO/auth-request branch:

let user = if let Some(email) = email {
    // email + master_password_hash flow, device_identifier never read
    ...
} else {
    // SSO flow only
    User::find_by_device_for_email2fa(&data.device_identifier, &conn).await
    ...
};

The upstream Bitwarden CLI's TwoFactorEmailRequest declares the field but login.command.ts never sets it before calling postTwoFactorEmail, so it serialises to nothing. The official server tolerates the omission, so the failure only shows up on vaultwarden.

Suggested fix

Make device_identifier: Option<DeviceId> and require it only in the SSO branch:

struct SendEmailLoginData {
    #[serde(alias = "DeviceIdentifier")]
    device_identifier: Option<DeviceId>,
    ...
}

let user = if let Some(email) = email {
    // unchanged
    ...
} else {
    let Some(device_identifier) = &data.device_identifier else {
        err!("Either email+masterPasswordHash or deviceIdentifier+authRequest must be supplied.")
    };
    User::find_by_device_for_email2fa(device_identifier, &conn).await
    ...
};

That matches what the email+master-password-hash flow already does (which never touches device_identifier) and keeps the SSO flow honest.

Impact

Any client that uses the upstream Bitwarden CLI to log in with email 2FA against vaultwarden is affected. Caught while building a Command Palette extension that wraps bw login. Happy to test a fix.

Originally created by @hoobio on GitHub (May 13, 2026). Original GitHub issue: https://github.com/dani-garcia/vaultwarden/issues/7212 ### Prerequisites - [X] I have searched the existing **Closed _AND_ Open** [Issues](https://github.com/dani-garcia/vaultwarden/issues?q=is%3Aissue%20) **_AND_** [Discussions](https://github.com/dani-garcia/vaultwarden/discussions?discussions_q=) - [X] I have searched and read the [documentation](https://github.com/dani-garcia/vaultwarden/wiki/) ### Vaultwarden Support String ### Your environment (Generated via diagnostics page) * Vaultwarden version: v1.36.0 * Web-vault version: v2026.4.1 * OS/Arch: linux/x86_64 * Running within a container: true (Base: Debian) * Database type: SQLite * Database version: 3.51.3 * Uses config.json: false * Uses a reverse proxy: true * IP Header check: true (CF-Connecting-IP) * Internet access: true * Internet access via a proxy: false * DNS Check: true * TZ environment: Australia/Melbourne * Browser/Server Time Check: true * Server/NTP Time Check: true * Domain Configuration Check: true * HTTPS Check: true * Websocket Check: false * HTTP Response Checks: true ### Config & Details (Generated via diagnostics page) <details><summary>Show Config & Details</summary> **Config:** ```json { "_duo_akey": null, "_enable_duo": true, "_enable_email_2fa": true, "_enable_smtp": true, "_enable_yubico": true, "_icon_service_csp": "", "_icon_service_url": "", "_ip_header_enabled": true, "_max_note_size": 10000, "_smtp_img_src": "***:", "admin_ratelimit_max_burst": 3, "admin_ratelimit_seconds": 300, "admin_session_lifetime": 20, "admin_token": "***", "allowed_connect_src": "", "allowed_iframe_ancestors": "", "attachments_folder": "data/attachments", "auth_request_purge_schedule": "30 * * * * *", "authenticator_disable_time_drift": false, "data_folder": "data", "database_conn_init": "", "database_idle_timeout": 600, "database_max_conns": 10, "database_min_conns": 2, "database_timeout": 30, "database_url": "***************", "db_connection_retries": 15, "disable_2fa_remember": false, "disable_admin_token": true, "disable_icon_download": false, "dns_prefer_ipv6": false, "domain": "*****://**************", "domain_origin": "*****://**************", "domain_path": "", "domain_set": true, "duo_context_purge_schedule": "30 * * * * *", "duo_host": null, "duo_ikey": null, "duo_skey": null, "duo_use_iframe": false, "email_2fa_auto_fallback": false, "email_2fa_enforce_on_verified_invite": false, "email_attempts_limit": 3, "email_change_allowed": true, "email_expiration_time": 600, "email_token_size": 6, "emergency_access_allowed": true, "emergency_notification_reminder_schedule": "0 3 * * * *", "emergency_request_timeout_schedule": "0 7 * * * *", "enable_db_wal": true, "enable_websocket": true, "enforce_single_org_with_reset_pw_policy": false, "event_cleanup_schedule": "0 10 0 * * *", "events_days_retain": null, "experimental_client_feature_flags": "", "extended_logging": true, "helo_name": null, "hibp_api_key": null, "http_request_block_non_global_ips": true, "http_request_block_regex": null, "icon_blacklist_non_global_ips": true, "icon_blacklist_regex": null, "icon_cache_folder": "data/icon_cache", "icon_cache_negttl": 259200, "icon_cache_ttl": 2592000, "icon_download_timeout": 10, "icon_redirect_code": 302, "icon_service": "internal", "incomplete_2fa_schedule": "30 * * * * *", "incomplete_2fa_time_limit": 3, "increase_note_size_limit": false, "invitation_expiration_hours": 120, "invitation_org_name": "Vaultwarden", "invitations_allowed": true, "ip_header": "CF-Connecting-IP", "job_poll_interval_ms": 30000, "log_file": "/data/vaultwarden.log", "log_level": "info", "log_timestamp_format": "%Y-%m-%d %H:%M:%S.%3f", "login_ratelimit_max_burst": 10, "login_ratelimit_seconds": 60, "org_attachment_limit": null, "org_creation_users": "", "org_events_enabled": false, "org_groups_enabled": false, "password_hints_allowed": true, "password_iterations": 600000, "purge_incomplete_sso_auth": "0 20 0 * * *", "push_enabled": false, "push_identity_uri": "https://identity.bitwarden.com", "push_installation_id": "***", "push_installation_key": "***", "push_relay_uri": "https://push.bitwarden.com", "reload_templates": false, "require_device_email": false, "rsa_key_filename": "data/rsa_key", "send_purge_schedule": "0 5 * * * *", "sendmail_command": null, "sends_allowed": true, "sends_folder": "data/sends", "show_password_hint": false, "signups_allowed": false, "signups_domains_whitelist": "", "signups_verify": false, "signups_verify_resend_limit": 6, "signups_verify_resend_time": 3600, "smtp_accept_invalid_certs": false, "smtp_accept_invalid_hostnames": false, "smtp_auth_mechanism": "Login", "smtp_debug": false, "smtp_embed_images": true, "smtp_explicit_tls": null, "smtp_from": "**************", "smtp_from_name": "*****", "smtp_host": "******************", "smtp_password": "***", "smtp_port": 587, "smtp_security": "starttls", "smtp_ssl": null, "smtp_timeout": 15, "smtp_username": "*************", "sso_allow_unknown_email_verification": false, "sso_audience_trusted": null, "sso_auth_only_not_session": false, "sso_authority": "", "sso_authorize_extra_params": "", "sso_callback_path": "*****://*******************************************", "sso_client_cache_expiration": 0, "sso_client_id": "", "sso_client_secret": "***", "sso_debug_tokens": false, "sso_enabled": false, "sso_master_password_policy": null, "sso_only": false, "sso_pkce": true, "sso_scopes": "email profile", "sso_signups_match_email": true, "templates_folder": "data/templates", "tmp_folder": "data/tmp", "trash_auto_delete_days": null, "trash_purge_schedule": "0 5 0 * * *", "use_sendmail": false, "use_syslog": false, "user_attachment_limit": null, "user_send_limit": null, "web_vault_enabled": true, "web_vault_folder": "web-vault/", "yubico_client_id": null, "yubico_secret_key": null, "yubico_server": null } ``` </details> ### Vaultwarden Build Version v1.36.0 ### Deployment method Official Container Image ### Custom deployment method _n/a_ ### Reverse Proxy nginx (in front of Cloudflare Tunnel) + OWASP ModSecurity CRS sidecar in detection mode. Verified the WAF is not the cause: the 422 is generated by vaultwarden's data guard before the handler runs. ### Host/Server Operating System Linux ### Operating System Version Debian GNU/Linux 13 (trixie), kernel 6.17.13, x86_64. ### Clients - CLI ### Client Version Bitwarden CLI v2026.4.1 (Web-Vault 2026.4.1) ### Steps To Reproduce 1. Configure email 2FA on a vaultwarden account. 2. Run `bw login <email> --passwordenv BW_MP --method 1` (no `--code`) against vaultwarden, with `BW_MP` set to the master password. 3. CLI authenticates with the master password (`POST /identity/connect/token` returns 400 with `2FA token not provided`, as expected). 4. CLI calls `POST /api/two-factor/send-email-login` to trigger the code email. ### Expected Result vaultwarden sends the email code. The CLI then prompts for the token. ### Actual Result vaultwarden returns 422. No code email is ever sent. Roughly three minutes later the user receives the standard incomplete-2FA security warning email instead. ### Logs ``` [INFO] POST /api/two-factor/send-email-login [WARN] Data guard `Json < SendEmailLoginData >` failed: Parse("{\"email\":\"alex@example.com\",\"masterPasswordHash\":\"...\"}", Error("missing field `deviceIdentifier`", line: 1, column: 93)). [INFO] (send_email_login) POST /api/two-factor/send-email-login => 422 Unprocessable Entity ``` ### Screenshots or Videos _n/a_ ### Additional Context #### Root cause [`SendEmailLoginData`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/two_factor/email.rs) declares `device_identifier: DeviceId` (mandatory): ```rust struct SendEmailLoginData { #[serde(alias = "DeviceIdentifier")] device_identifier: DeviceId, email: Option<String>, master_password_hash: Option<String>, auth_request_id: Option<AuthRequestId>, auth_request_access_code: Option<String>, } ``` But `device_identifier` is **only used in the SSO/auth-request branch**: ```rust let user = if let Some(email) = email { // email + master_password_hash flow, device_identifier never read ... } else { // SSO flow only User::find_by_device_for_email2fa(&data.device_identifier, &conn).await ... }; ``` The upstream Bitwarden CLI's [`TwoFactorEmailRequest`](https://github.com/bitwarden/clients/blob/main/libs/common/src/auth/models/request/two-factor-email.request.ts) declares the field but [`login.command.ts`](https://github.com/bitwarden/clients/blob/main/apps/cli/src/auth/commands/login.command.ts) never sets it before calling `postTwoFactorEmail`, so it serialises to nothing. The official server tolerates the omission, so the failure only shows up on vaultwarden. #### Suggested fix Make `device_identifier: Option<DeviceId>` and require it only in the SSO branch: ```rust struct SendEmailLoginData { #[serde(alias = "DeviceIdentifier")] device_identifier: Option<DeviceId>, ... } let user = if let Some(email) = email { // unchanged ... } else { let Some(device_identifier) = &data.device_identifier else { err!("Either email+masterPasswordHash or deviceIdentifier+authRequest must be supplied.") }; User::find_by_device_for_email2fa(device_identifier, &conn).await ... }; ``` That matches what the email+master-password-hash flow already does (which never touches `device_identifier`) and keeps the SSO flow honest. #### Impact Any client that uses the upstream Bitwarden CLI to log in with email 2FA against vaultwarden is affected. Caught while building [a Command Palette extension](https://github.com/hoobio/command-palette-bitwarden) that wraps `bw login`. Happy to test a fix.
Author
Owner

@BlackDex commented on GitHub (May 13, 2026):

Please provide the support string as requested. All environment details which should be private are masked or omitted. That is the whole reason we ask for it and created it like that.

<!-- gh-comment-id:4437926402 --> @BlackDex commented on GitHub (May 13, 2026): Please provide the support string as requested. All environment details which should be private are masked or omitted. That is the whole reason we ask for it and created it like that.
Author
Owner

@hoobio commented on GitHub (May 13, 2026):

@BlackDex Support string added to the issue body, sorry for the omission

<!-- gh-comment-id:4438016716 --> @hoobio commented on GitHub (May 13, 2026): @BlackDex Support string added to the issue body, sorry for the omission
Author
Owner

@hoobio commented on GitHub (May 13, 2026):

Happy to put up a PR if you'd like. Wanted to confirm the suggested shape lands well first: device_identifier: Option<DeviceId>, validated only in the SSO / auth-request branch (the email + master-password-hash branch never reads it today). Any other call sites in this handler that rely on it being mandatory I should mirror?

<!-- gh-comment-id:4438136454 --> @hoobio commented on GitHub (May 13, 2026): Happy to put up a PR if you'd like. Wanted to confirm the suggested shape lands well first: `device_identifier: Option<DeviceId>`, validated only in the SSO / auth-request branch (the email + master-password-hash branch never reads it today). Any other call sites in this handler that rely on it being mandatory I should mirror?
Author
Owner

@BlackDex commented on GitHub (May 13, 2026):

I doubt it is only validated during the SSO flow, AI Has it wrong, because if that was the case, web-vault and other clients like desktop, browser extensions would have had this issue too, which is not the case.

<!-- gh-comment-id:4439384807 --> @BlackDex commented on GitHub (May 13, 2026): I doubt it is only validated during the SSO flow, AI Has it wrong, because if that was the case, web-vault and other clients like desktop, browser extensions would have had this issue too, which is not the case.
Author
Owner

@hoobio commented on GitHub (May 13, 2026):

@BlackDex The GUI clients DO send deviceIdentifier, it's just the CLI that doesn't (and iis probably a genuine upstream bug).
e.g. Captured both bodies for the same POST /api/two-factor/send-email-login flow on the same (vaultwarden) account:

[bw CLI 2026.4.1]
{"email":"...","masterPasswordHash":"..."}

[Web vault]
{"email":"...","masterPasswordHash":"...","ssoEmail2FaSessionToken":"","deviceIdentifier":"99ce32d5-...","authRequestAccessCode":"","authRequestId":""}

Confirmed against vault.bitwarden.com too: identical CLI body, the official server accepts it and the code email arrives.
Whereas vaultwarden rejects the same request with 422.

I'll log an issue in the official bitwarden/clients repo for this gap.
Open question on your end: there's still a behavioural delta where Bitwarden Server tolerates the missing field and vaultwarden rejects it. If you'd like to keep that strict (push the fix to the CLI side and let stale callers feel the 422 until they update), totally fair, and honestly the correct path (surprised official bitwarden doesn't validate it). Your call.

<!-- gh-comment-id:4439715553 --> @hoobio commented on GitHub (May 13, 2026): @BlackDex The GUI clients DO send `deviceIdentifier`, it's just the CLI that doesn't (and iis probably a genuine upstream bug). e.g. Captured both bodies for the same `POST /api/two-factor/send-email-login` flow on the same (vaultwarden) account: [bw CLI 2026.4.1] `{"email":"...","masterPasswordHash":"..."}` [Web vault] `{"email":"...","masterPasswordHash":"...","ssoEmail2FaSessionToken":"","deviceIdentifier":"99ce32d5-...","authRequestAccessCode":"","authRequestId":""}` Confirmed against vault.bitwarden.com too: identical CLI body, the official server accepts it and the code email arrives. Whereas vaultwarden rejects the same request with 422. I'll log an issue in the official [bitwarden/clients](https://github.com/bitwarden/clients) repo for this gap. Open question on your end: *there's still a behavioural delta where Bitwarden Server tolerates the missing field and vaultwarden rejects it*. If you'd like to keep that strict (push the fix to the CLI side and let stale callers feel the 422 until they update), totally fair, and honestly the correct path (surprised official bitwarden doesn't validate it). Your call.
Author
Owner

@BlackDex commented on GitHub (May 13, 2026):

I think this can be changed to an Option<>, my guess would be that this wasn't tested by @stefan0xC when fixing the SSO issue.

<!-- gh-comment-id:4442719454 --> @BlackDex commented on GitHub (May 13, 2026): I think this can be changed to an `Option<>`, my guess would be that this wasn't tested by @stefan0xC when fixing the SSO issue.
Author
Owner

@stefan0xC commented on GitHub (May 14, 2026):

Indeed. I was assuming that all clients send a device identifier for this endpoint when I undid #6473 for SSO again with #6495. Before the SSO PR the device_identifer was commented out as unused, so I think it makes sense to make it optional. (Though this will still fail for SSO users if this call neither provides a device id nor an email address...)

<!-- gh-comment-id:4455653683 --> @stefan0xC commented on GitHub (May 14, 2026): Indeed. I was assuming that all clients send a device identifier for this endpoint when I undid #6473 for SSO again with #6495. Before the SSO PR the `device_identifer` was commented out as unused, so I think it makes sense to make it optional. (Though this will still fail for SSO users if this call neither provides a device id nor an email address...)
Author
Owner

@stefan0xC commented on GitHub (May 15, 2026):

I've looked a bit into it (with mitmproxy) and the device identifier is only sent as request header Device-Identifier. This is probably sent by all clients (so we could probably rewrite the way we get the device identifier) but given that the actual problem is that the email and master_password_hash fields are empty when using SSO I still think this is a bug in the client (or maybe a design flaw with the SSO login strategy?).

The other question is whether it would be fine to make this less strict since upstream does not seem to require either? I am wondering if the Bitwarden server does anything different so that an email can be sent or if it actually fails as well? Since SSO is an enterprise feature and they don't recommend enabling two factor for SSO I am not sure if someone has run into this. As far as I've looked they are seemingly fine with the CLI not working with some 2FA providers or if you are not using a master password at all, cf. https://github.com/bitwarden/clients/issues/18992 so maybe SSO with master password and email as 2FA just does not work?

<!-- gh-comment-id:4458144706 --> @stefan0xC commented on GitHub (May 15, 2026): I've looked a bit into it (with `mitmproxy`) and the device identifier is only sent as request header `Device-Identifier`. This is probably sent by all clients (so we could probably rewrite the way we get the device identifier) but given that the actual problem is that the `email` and `master_password_hash` fields are empty when using `SSO` I still think this is a bug in the client (or maybe a design flaw with the SSO login strategy?). The other question is whether it would be fine to make this less strict since upstream does not seem to require either? I am wondering if the Bitwarden server does anything different so that an email can be sent or if it actually fails as well? Since SSO is an enterprise feature and they don't recommend enabling two factor for SSO I am not sure if someone has run into this. As far as I've looked they are seemingly fine with the CLI not working with some 2FA providers or if you are not using a master password at all, cf. https://github.com/bitwarden/clients/issues/18992 so maybe SSO with master password and email as 2FA just does not work?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/vaultwarden#30137