bitwarden/server · error · BadRequestException

Please provide a device identifier

Error message

Please provide a device identifier

What it means

Thrown by POST /devices/lost-trust when the authenticated principal's device identifier is null in the request context. The endpoint exists so a client that holds a device key but never received decryption keys can log the trust-loss event; it requires the caller to be identified as a specific device. The device identifier is normally extracted from the device (access) token by middleware and surfaced on CurrentContext.

Source

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

        }

        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)
        {
            throw new UnauthorizedAccessException();
        }

        var deviceId = _currentContext.DeviceIdentifier;
        if (deviceId == null)
        {
            throw new BadRequestException("Please provide a device identifier");
        }

        var deviceType = _currentContext.DeviceType;
        if (deviceType == null)
        {
            throw new BadRequestException("Please provide a device type");
        }

        _logger.LogError("User {id} has a device key, but didn't receive decryption keys for device {device} of type {deviceType}", userId,
            deviceId, deviceType);
    }

}

View on GitHub (pinned to e93b962371)

Solutions

  1. Re-authenticate using a device (access) token that carries the device identifier claim, not a user-scoped token.
  2. Ensure the device is registered first (POST /devices) so a device token with the identifier is issued.
  3. Verify the Authorization header carries a device token; check CurrentContext.DeviceIdentifier is populated before calling.

Example fix

// before: user token, no device claim -> DeviceIdentifier is null
//   POST /devices/lost-trust  Authorization: Bearer <user-access-token>
//
// after: use the device access token obtained at device registration
await deviceClient.PostLostTrustAsync(); // deviceClient uses the device token
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the caller's token carries a device identifier before calling lost-trust
const deviceId = await getDeviceClaimFromToken(accessToken);
if (!deviceId) {
  // re-authenticate with a device token or register a device first
  await registerDevice();
  return;
}
await devicesApi.postLostTrust();

Type guard

function hasDeviceIdentifier(claims) {
  return claims?.deviceidentifier != null && claims.deviceidentifier.trim() !== '';
}

Try / catch

try {
  await devicesApi.postLostTrust();
} catch (e) {
  if (e.status === 400 && /device identifier/i.test(e.message)) {
    // token lacks device claim: re-register / re-authenticate as a device
    await registerDeviceAndRetry();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling POST /devices/lost-trust authenticated as a user (with a user token) rather than a device token, or with an access token that has no device claim. UserId is present and non-default, but DeviceIdentifier on _currentContext is null.

Common situations: Client authenticated via a user session/OAuth token instead of a device token; a stale token minted before device claims were issued; a test harness that builds a token without the device identifier claim.

Related errors


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