bitwarden/server · warning · BadRequestException

Please provide an email and device identifier

Error message

Please provide an email and device identifier

What it means

Thrown by GET /knowndevice/{email}/{identifier} (and the header-based GetByIdentifierQuery) when either the email or device identifier resolves to null, empty, or whitespace. The route exists to let a client check whether a device is already known before login; it refuses to query the repository with a blank argument. The path form is deprecated because URL encoding corrupts emails, so callers should use the X-Request-Email / X-Device-Identifier header form.

Source

Thrown at src/Api/Controllers/DevicesController.cs:297

    {
        await Deactivate(id);
    }

    [AllowAnonymous]
    [HttpGet("knowndevice")]
    public async Task<bool> GetByIdentifierQuery(
            [Required][FromHeader(Name = "X-Request-Email")] string Email,
            [Required][FromHeader(Name = "X-Device-Identifier")] string DeviceIdentifier)
        => await GetByEmailAndIdentifier(CoreHelpers.Base64UrlDecodeString(Email), DeviceIdentifier);

    [Obsolete("Path is deprecated due to encoding issues, use /knowndevice instead.")]
    [AllowAnonymous]
    [HttpGet("knowndevice/{email}/{identifier}")]
    public async Task<bool> GetByEmailAndIdentifier(string email, string identifier)
    {
        if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(identifier))
        {
            throw new BadRequestException("Please provide an email and device identifier");
        }

        var user = await _userRepository.GetByEmailAsync(email);
        if (user == null)
        {
            return false;
        }

        var device = await _deviceRepository.GetByIdentifierAsync(identifier, user.Id);
        return device != null;
    }

    [HttpPost("lost-trust")]
    public void PostLostTrust()
    {
        var userId = _currentContext.UserId.GetValueOrDefault();
        if (userId == default)
        {

View on GitHub (pinned to e93b962371)

Solutions

  1. Send both a non-empty, non-whitespace email (base64url-encoded for X-Request-Email) and a non-empty device identifier.
  2. Migrate off the deprecated /knowndevice/{email}/{identifier} path form to the header-based endpoint to avoid email-encoding corruption.
  3. Base64url-encode the email client-side before placing it in X-Request-Email (the controller calls CoreHelpers.Base64UrlDecodeString on it).
  4. Trim and validate both values on the client before issuing the request.

Example fix

// before: header value is whitespace
//   X-Request-Email: "   "
//   X-Device-Identifier: "  "
//
// after: base64url-encode a real email, supply real identifier
var emailB64 = base64urlEncode("user@example.com"); // e.g. "dXNlckBleGFtcGxlLmNvbQ"
client.get("/devices/knowndevice", {
  headers: { "X-Request-Email": emailB64, "X-Device-Identifier": deviceId }
});
Defensive patterns

Strategy: validation

Validate before calling

// Validate before calling /devices/knowndevice
function canQueryKnownDevice(email, identifier) {
  if (!email || !email.trim()) return false;
  if (!identifier || !identifier.trim()) return false;
  try { base64urlDecode(email); } catch { return false; } // header form expects base64url
  return true;
}
if (canQueryKnownDevice(email, deviceId)) {
  await client.getByIdentifierQuery(email, deviceId);
}

Type guard

function isNonEmptyString(v) { return typeof v === 'string' && v.trim().length > 0; }

Prevention

When it happens

Trigger: Hitting GET /knowndevice with a whitespace-only email or identifier segment, or calling the header form with X-Request-Email / X-Device-Identifier set to spaces. The [Required] attribute rejects null and empty string but lets pure-whitespace values through, so only the IsNullOrWhiteSpace guard inside catches them. Also fires if a client base64url-decodes to an empty string.

Common situations: Clients migrating off the deprecated path form and sending raw (non-base64url) headers by mistake; device-provisioning scripts that send a placeholder ' ' while bootstrapping; URL-encoded emails that decode to empty.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/620761468795fed0. Report an issue: GitHub.