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 OAuth2 SSO recover-success controller when the browser-only OAuth landing endpoint receives a JSON/Ajax request. Like its Azure/Google counterparts, this endpoint expects a full-page browser navigation following the OAuth2 provider redirect and rejects JSON requests up front.

Solutions

  1. Use plain browser navigation to the success URL; remove JSON/Ajax headers from the request.
  2. Do not call this endpoint programmatically; only follow the OAuth2 provider's redirect.
  3. Automate with a headless browser or a plain HTTP client that follows redirects without JSON headers.
  4. Inspect proxies/extensions that may convert the navigation into an Ajax request.

Example fix

// before
fetch('/sso/recover/success?token=...', { headers: { 'Accept': 'application/json' } });
// after
window.location.href = '/sso/recover/success?token=...';
Defensive patterns

Strategy: validation

Validate before calling

const accept = headers.get('Accept') ?? '';
if (accept.includes('application/json')) {
  throw new Error('OAuth2 SSO success endpoint rejects JSON/Ajax requests; use browser navigation.');
}

Type guard

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

Try / catch

try {
  const res = await fetch(successUrl, { redirect: 'follow' });
  if (res.status === 400 && (await res.text()).includes('Ajax/Json request not supported')) {
    window.location.href = successUrl;
  }
} catch (e) { window.location.href = successUrl; }

Prevention

When it happens

Trigger: GET /sso/recover/success?token=... (OAuth2 variant) with Accept: application/json, X-Requested-With: XMLHttpRequest, or a JSON method-override header, e.g. when the URL is fetched by the passbolt JS API client or a script rather than loaded in the browser.

Common situations: curl/postman tests carrying JSON headers; a fetch/XHR call to the callback URL instead of following the OAuth redirect in the browser; browser extension or service worker rewriting the redirect.

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/283b16f1fc38a263. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/SsoRecover/src/Controller/OAuth2/OAuth2RecoverSuccessController.php:45

class OAuth2RecoverSuccessController extends AbstractSsoController
{
    /**
     * @inheritDoc
     */
    public function beforeFilter(EventInterface $event)
    {
        parent::beforeFilter($event);
        $this->Authentication->allowUnauthenticated(['ssoRecoverSuccess']);
    }

    /**
     * @return void
     */
    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)