passbolt/passbolt_api · warning · BadRequestException
The request is invalid.
Error message
The request is invalid.
What it means
Thrown by SsoKeysGetController::get() as a deliberately generic 400. Any BadRequestException raised while verifying the SSO key token (including errors surfaced by SsoKeysGetService::get()) is swallowed and replaced with 'The request is invalid.' to prevent attackers from enumerating valid tokens or key ids. The original exception is chained for server-side logging.
Solutions
- Restart the complete SSO login flow (stage1) to obtain a fresh token and retry verification promptly
- Verify the verification URL contains the exact keyId and token issued in the current flow, not from a prior attempt
- Check server logs for the chained exception to see the true underlying cause
- Ensure clocks are synced — token expiry checks depend on server time
- If using an extension/client, update it — older versions may generate incompatible tokens
Example fix
// before
// reusing a token from an aborted flow
await api.get(`/sso/keys/${oldKeyId}?token=${oldToken}`);
// after
const {keyId, token} = await startSsoStage1(); // fresh flow
await api.get(`/sso/keys/${keyId}?token=${token}`); Defensive patterns
Strategy: try-catch
Validate before calling
// cannot pre-validate the token server-side; ensure token and keyId are from the CURRENT stage1 response
if (!currentFlowToken || !currentFlowKeyId) throw new Error('Start a fresh SSO stage1 flow before verifying the key'); Try / catch
try {
await verifySsoKey(keyId, token);
} catch (e) {
if (e.response?.status === 400) {
// generic by design: restart the full SSO flow for a fresh token; check server logs for details
} else if (e.response?.status === 404) {
// key could not be found
}
} Prevention
- Always use the token/keyId from the most recent stage1 response; never cache across flows
- Complete verification promptly to stay within token TTL
- Sync server clocks to avoid spurious token expiry
- Read server logs for the chained exception to diagnose the real cause
- Update browser extensions/clients to the latest SSO protocol version
When it happens
Trigger: GET /sso/keys with an invalid, expired, malformed, or already-consumed verification token, or a mismatched keyId/user combination — anything the underlying service rejects as BadRequest.
Common situations: User took too long between stage1 and token verification so the token expired; user restarted the SSO flow and reused an old token from a previous attempt; manually constructed verification URL with wrong key id; browser extension replaying stale tokens.
Related errors
- The authentication token does not exist or has been deleted.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/b9c20d2d788310e3.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Controller/Keys/SsoKeysGetController.php:67
* @return void
*/
public function get(string $keyId, string $userId, string $token): void
{
$this->User->assertNotLoggedIn();
try {
$user = (new UserGetService())->getActiveNotDeletedNotDisabledOrFail($userId);
$uac = new ExtendedUserAccessControl(
Role::GUEST,
$userId,
$user->username,
$this->User->ip(),
$this->User->userAgent()
);
$key = (new SsoKeysGetService())->get($uac, $token, $keyId);
} catch (BadRequestException $exception) {
// Hide error details to prevent enumerations
throw new BadRequestException(__('The request is invalid.'), 400, $exception);
} catch (RecordNotFoundException $exception) {
throw new NotFoundException(__('The key could not be found'), 404, $exception);
}
$this->success(__('The operation was successful'), $key);
}
}
View on GitHub (pinned to 31c1bbc10f)