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

  1. Enable the SsoRecover plugin in config/passbolt.php (plugins.SsoRecover.enabled = true, assuming EE license supports it) and clear cache.
  2. Re-run the SSO setup/health check to confirm the plugin loads: `ddev refresh` or passbolt healthcheck.
  3. If SsoRecover is not licensed/needed, disable SSO-based recovery flows and use the standard username-based recovery process.
  4. 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

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


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)