bitwarden/server · error · BadRequestException
The token associated with your request is expired. A valid t
Error message
The token associated with your request is expired. A valid token is required to continue.
What it means
BadRequestException 'The token associated with your request is expired...' is thrown in POST /webauthn (WebAuthnController.Post) when _createOptionsDataProtector.Unprotect(model.Token) succeeds but tokenable.TokenIsValid(user) returns false. The token is a data-protection-protected creation-options payload bound to the user; invalidity means it expired or is bound to a different user.
Source
Thrown at src/Api/Auth/Controllers/WebAuthnController.cs:111
return new WebAuthnLoginAssertionOptionsResponseModel
{
Options = options,
Token = token
};
}
[Authorize(Policies.Application)]
[HttpPost("")]
public async Task<WebAuthnCredentialResponseModel> Post([FromBody] WebAuthnLoginCredentialCreateRequestModel model)
{
var user = await GetUserAsync();
await ValidateIfUserCanUsePasskeyLogin(user.Id);
var tokenable = _createOptionsDataProtector.Unprotect(model.Token);
if (!tokenable.TokenIsValid(user))
{
throw new BadRequestException("The token associated with your request is expired. A valid token is required to continue.");
}
var credential = await _createWebAuthnLoginCredentialCommand.CreateWebAuthnLoginCredentialAsync(user, model.Name, tokenable.Options, model.DeviceResponse, model.SupportsPrf, model.EncryptedUserKey, model.EncryptedPublicKey, model.EncryptedPrivateKey);
if (credential == null)
{
throw new BadRequestException("Unable to complete WebAuthn registration.");
}
return new WebAuthnCredentialResponseModel(credential);
}
private async Task ValidateIfUserCanUsePasskeyLogin(Guid userId)
{
var requireSsoPolicyRequirement = await _policyRequirementQuery.GetAsyncVNext<RequireSsoPolicyRequirement>(userId);
if (!requireSsoPolicyRequirement.CanUsePasskeyLogin)
{
throw new BadRequestException("Passkeys cannot be created for your account. SSO login is required.");View on GitHub (pinned to e93b962371)
Solutions
- Request fresh creation options (POST /webauthn/options or equivalent) and immediately complete registration.
- Use the same authenticated user for options request and credential creation.
- On self-hosted, ensure data-protection keys are persisted/consistent across restarts.
- Do not cache the token; treat it as short-lived.
Example fix
// before
api.post('/webauthn', { token: cachedOptionsToken, deviceResponse })
// after
const { token } = await api.post('/webauthn/options');
const deviceResponse = await navigator.credentials.create({ publicKey: parseOptions(token) });
api.post('/webauthn', { token, name, deviceResponse }); Defensive patterns
Strategy: validation
Validate before calling
if (!model.token) { model.token = (await api.post('/webauthn/options')).token; } Try / catch
try { await api.post('/webauthn', model); }
catch (e) {
if (e.response?.status === 400 && /expired/.test(e.response.data?.message)) {
model.token = (await api.post('/webauthn/options')).token;
throw new RetryableError('Restart passkey creation with a fresh token', model);
}
throw e;
} Prevention
- Request creation options and complete the ceremony without delay.
- Persist data-protection keys on self-hosted deployments.
- Use the same authenticated user throughout.
When it happens
Trigger: POST /webauthn (passkey creation, line 111) where model.Token is expired, reused after consumption, or minted for a different user than the current principal. The user must also pass ValidateIfUserCanUsePasskeyLogin first.
Common situations: Delay between requesting creation options and submitting the device response, user switched accounts, token cached client-side, or the data-protection keys rotated on a self-hosted reinstall.
Related errors
- The token associated with your request is invalid or has exp
- InvalidSsoToken
- Invalid token.
- Unable to complete WebAuthn registration.
- Passkeys cannot be created for your account. SSO login is re
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/11b9d1d6cf2e056d.
Report an issue: GitHub.