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 Azure SSO recover-success controller when a client sends the request with a JSON/Ajax Accept or Content-Type header. The SSO recover success endpoint is a browser redirect target (full-page HTML flow) and is deliberately not part of the JSON API, so any JSON-formatted request is rejected immediately before any token validation.

Solutions

  1. Remove JSON-related headers (Accept: application/json, X-Requested-With, X-Http-Method-Override) and request the endpoint as a plain browser navigation/redirect.
  2. Do not invoke the recover-success URL from API code; only follow the OAuth2 provider redirect in a browser.
  3. If automating the flow, use a headless browser or follow redirects with a plain HTTP client that does not set JSON headers.
  4. If you landed here from the JS API, switch to the regular (non-JSON) fetch or window.location navigation.

Example fix

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

Strategy: validation

Validate before calling

const url = new URL(successUrl, window.location.origin);
const isJsonRequest = headers.has('Accept') && headers.get('Accept').includes('application/json');
if (isJsonRequest) throw new Error('Use browser navigation, not a JSON request, for the SSO success endpoint.');

Type guard

function isAjaxHeader(headers) {
  return headers instanceof Headers &&
    (headers.get('X-Requested-With') === 'XMLHttpRequest' ||
     (headers.get('Accept') ?? '').includes('application/json'));
}

Try / catch

try {
  const res = await fetch('/sso/recover/success/azure?token=' + token, { redirect: 'follow' });
  if (res.status === 400 && (await res.text()).includes('Ajax/Json request not supported')) {
    window.location.href = '/sso/recover/success/azure?token=' + token;
  }
} catch (e) { /* network errors */ }

Prevention

When it happens

Trigger: A client calls GET /sso/recover/success/azure (the OAuth2 redirect landing URL) with the 'X-Http-Method-Override' or Accept header indicating JSON (e.g. the passbolt JS API client default headers, Accept: application/json), or an Ajax/fetch call is made against this browser-only endpoint.

Common situations: Developers testing the SSO recover flow via curl/postman with passbolt's typical JSON headers, or a browser extension/script intercepting the OAuth redirect and re-issuing it as an Ajax request instead of a full navigation.

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

Appendix: source

Thrown at plugins/PassboltEe/SsoRecover/src/Controller/Azure/AzureRecoverSuccessController.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)