passbolt/passbolt_api · error · BadRequestException

Ajax/Json request not supported.

Error message

Ajax/Json request not supported.

What it means

This BadRequestException is thrown by the Google SSO recover-success controller when a request to the browser-only OAuth redirect landing endpoint arrives as JSON/Ajax. The endpoint is designed for full-page browser navigation after Google's OAuth callback, so JSON-formatted requests are rejected before token processing.

Solutions

  1. Request the endpoint with plain browser navigation; strip Accept: application/json and X-Requested-With headers.
  2. Never call the recover-success URL from API client code; let the OAuth2 provider's redirect land in the browser.
  3. If automating, use a headless browser (Playwright/Puppeteer) or a redirect-following client without JSON headers.
  4. Check proxies/extensions that may rewrite the redirect into an Ajax call.

Example fix

// before
axios.get('/sso/recover/success/google?token=...'); // sends Accept: application/json
// after
window.location.href = '/sso/recover/success/google?token=...';
Defensive patterns

Strategy: validation

Validate before calling

const isJson = headers.get('Accept')?.includes('application/json');
if (isJson) throw new Error('The Google SSO success endpoint must be loaded via browser navigation, not fetch/Ajax.');

Type guard

function isBrowserNavigation(init) {
  const h = new Headers(init?.headers);
  return !h.has('X-Requested-With') && !(h.get('Accept') ?? '').includes('application/json');
}

Try / catch

try {
  const res = await fetch(url, { redirect: 'follow' });
  if (res.status === 400) {
    window.location.href = url; // fall back to full navigation
  }
} catch (e) { window.location.href = url; }

Prevention

When it happens

Trigger: GET /sso/recover/success/google?token=... with Accept: application/json or Ajax markers (X-Requested-With: XMLHttpRequest), typically from the passbolt JS client or a fetch/axios call instead of a browser redirect.

Common situations: Testing the Google SSO recovery URL via curl/postman with default passbolt JSON headers; a script or service worker intercepting the OAuth callback and re-issuing it as an Ajax request.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/1dafe71a4cf7805e. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/SsoRecover/src/Controller/Google/GoogleRecoverSuccessController.php:47

{
    /**
     * @inheritDoc
     */
    public function beforeFilter(EventInterface $event)
    {
        parent::beforeFilter($event);

        $this->Authentication->allowUnauthenticated(['ssoRecoverSuccess']);
    }

    /**
     * @return void
     * @throws \League\OAuth2\Client\Provider\Exception\IdentityProviderException
     */
    public function ssoRecoverSuccess(): void
    {
        if ($this->request->is('json')) {
            throw new BadRequestException(__('Ajax/Json request not supported.'));
        }

        $this->User->assertNotLoggedIn();
        $token = $this->getTokenFromUrlQuery();

        try {
            (new SsoAuthenticationTokenGetService())->getActiveNotExpiredOrFail($token, SsoState::TYPE_SSO_RECOVER);
        } catch (RecordNotFoundException $e) {
            throw new BadRequestException(
                __('The authentication token does not exist or has been deleted.'),
                null,
                $e
            );
        } catch (CustomValidationException $e) {
            throw new BadRequestException(
                __('The authentication token has been expired.'),
                null,
                $e

View on GitHub (pinned to 31c1bbc10f)