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
- Re-authenticate using a device (access) token that carries the device identifier claim, not a user-scoped token.
- Ensure the device is registered first (POST /devices) so a device token with the identifier is issued.
- 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
- Authenticate device-scoped endpoints with a device (access) token, not a user token.
- Register the device before invoking trust endpoints so the token carries the identifier claim.
- In tests, mint a token that includes the deviceidentifier claim.
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
- Please provide a device type
- Please provide an email and device identifier
- ExternalId cannot exceed 300 characters.
- ExternalId cannot exceed 300 characters.
- result.AsError.Message
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/899acdbd3d4e265c.
Report an issue: GitHub.