passbolt/passbolt_api · error · BadRequestException
SsoRecover plugin is disabled.
Error message
SsoRecover plugin is disabled.
What it means
During stage-2 SSO processing, if the SSO state type is TYPE_SSO_RECOVER (account recovery flow), the controller requires the separate SsoRecover plugin to be enabled. When isFeaturePluginEnabled('SsoRecover') is false, it throws BadRequestException 'SsoRecover plugin is disabled.' (HTTP 400).
Solutions
- Enable the SsoRecover plugin in config/passbolt.php (plugins.SsoRecover.enabled = true, assuming EE license supports it) and clear cache.
- Re-run the SSO setup/health check to confirm the plugin loads: `ddev refresh` or passbolt healthcheck.
- If SsoRecover is not licensed/needed, disable SSO-based recovery flows and use the standard username-based recovery process.
- Ensure all nodes in a cluster have identical plugin configuration.
Example fix
// before (config/passbolt.php) 'plugins' => ['Sso' => ['enabled' => true]] // after 'plugins' => ['Sso' => ['enabled' => true], 'SsoRecover' => ['enabled' => true]]
Defensive patterns
Strategy: validation
Validate before calling
// Admin precheck bin/cake passbolt healthcheck | grep -i sso // or config check // config/passbolt.php must contain: 'SsoRecover' => ['enabled' => true]
Try / catch
try {
await ssoRecoverCallback(url);
} catch (e) {
if (e.response && e.response.status === 400 && /SsoRecover plugin is disabled/.test(e.response.data?.detail ?? '')) {
fallbackToStandardRecovery();
}
} Prevention
- Enable SsoRecover in config/passbolt.php whenever SSO is enabled and recovery via SSO is offered.
- Run the passbolt healthcheck after enabling SSO to catch missing plugin config.
- Keep plugin configuration identical across all cluster nodes.
- Send users the standard recovery flow if SsoRecover is not licensed.
When it happens
Trigger: A user following an SSO recovery link while the SsoRecover plugin is not enabled on the server (missing from passbolt.plugins loaded / config, or EE license tier without it); recovery state created on an instance where the plugin was later disabled.
Common situations: Admin enabled SSO but forgot to enable SsoRecover; plugin disabled after a license downgrade; load balancer with mixed node configs where one node lacks the plugin; users clicking old recovery emails after the plugin was turned off.
Related errors
- Ajax/Json request not supported.
- The authentication token does not exist or has been deleted.
- The authentication token does not exist or has been deleted.
- The authentication token has been expired.
- The SSO settings do not exist.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/b4155810f916e33d.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Controller/AbstractSso2Stage2Controller.php:172
// To map 500(internal error/provider specific exceptions) to 4xx exception
if (isset($event->getResult()['customException'])) {
$e = $event->getResult()['customException'];
}
throw $e;
}
// Create SSO auth token for next step, e.g. get keys
$ssoAuthToken = $service->createAuthTokenToGetKey($uac, $service->getSettings()->id);
$successUrl = Router::url("/sso/login/success?token={$ssoAuthToken->token}", true);
// Triggers a successful user login event
$event = new Event(self::EVENT_USER_LOGIN_SUCCESS, $this, ['ssoService' => $service]);
$this->getEventManager()->dispatch($event);
break;
case SsoState::TYPE_SSO_RECOVER:
if (!$this->isFeaturePluginEnabled('SsoRecover')) {
throw new BadRequestException(__('SsoRecover plugin is disabled.'));
}
$ssoRecoverAssertService = new SsoRecoverAssertService();
try {
$successUrl = $ssoRecoverAssertService->assertAndGetRedirectUrl(
$service,
$ssoState,
$code,
$this->User->ip(),
$this->User->userAgent(),
$this->getProviderName()
);
} catch (Exception $e) {
$event = new Event(self::EVENT_PROVIDER_ERROR_RESOURCE_OWNER, $this, ['exception' => $e]);
$this->getEventManager()->dispatch($event);
// To map 500(internal error/provider specific exceptions) to 4xx exception
if (isset($event->getResult()['customException'])) {View on GitHub (pinned to 31c1bbc10f)