passbolt/passbolt_api · error · Passbolt\JwtAuthentication\Error\Exception\Challenge\InvalidDomainException

The domain is invalid. Expected

Error message

The domain is invalid. Expected: {0} and got {1}

What it means

Thrown by GpgJwtAuthenticator::assertDomain during GPG-based JWT challenge verification. The client-signed token payload must contain a 'domain' field matching the server's own full base URL. A mismatch means the token was signed for a different host, scheme, or port than the one receiving the request.

Solutions

  1. Set App.fullBaseUrl in config/app.php (or FULL_BASE_URL env) to the exact public URL clients use, e.g. https://passbolt.example.com
  2. Make the client use exactly the same base URL as fullBaseUrl, including scheme and port
  3. Check reverse-proxy headers (X-Forwarded-Proto/Host) so Router::url('/', true) resolves to the public URL
  4. Clear config cache after changing fullBaseUrl: bin/cake cache clear_all

Example fix

// before (config/app.php)
// 'fullBaseUrl' => false,
// after
'fullBaseUrl' => 'https://passbolt.example.com',
Defensive patterns

Strategy: validation

Validate before calling

// client-side, before signing
const expected = new URL(serverBaseUrl).origin + '/';
if (!tokenDomain.startsWith(expected)) throw new Error(`domain ${tokenDomain} != ${expected}`);

Try / catch

try { await passbolt.loginWithGpg(token); } catch (e) { if (String(e.message).includes('The domain is invalid')) { await fixFullBaseUrl(); } else { throw e; } }

Prevention

When it happens

Trigger: A GPG JWT login (POST /auth/jwt/login with an X-GPGAuth token) where the challenge/token 'domain' claim does not equal rtrim(Router::url('/', true), '/').

Common situations: Server moved behind a reverse proxy so the computed base URL includes https/host headers the client didn't use; App.fullBaseUrl not set so CakePHP guesses the URL; client configured with http:// instead of https:// or a different hostname (localhost vs 127.0.0.1 vs FQDN); trailing-slash or port mismatches.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php:450

    }

    /**
     * Assert domain
     *
     * @param mixed $domain domain
     * @return void
     * @throws \Passbolt\JwtAuthentication\Error\Exception\Challenge\InvalidDomainException if domain is invalid
     */
    public function assertDomain(mixed $domain): void
    {
        if (!isset($domain) || !is_string($domain)) {
            throw new InvalidDomainException(__('The domain is invalid.'));
        }

        if (rtrim($domain, '/') !== rtrim(Router::url('/', true), '/')) {
            $expect = rtrim(Router::url('/', true));
            $got = rtrim($domain, '/');
            throw new InvalidDomainException(__('The domain is invalid. Expected: {0} and got {1}', $expect, $got));
        }
    }

    /**
     * @return \App\Utility\OpenPGP\OpenPGPBackend
     */
    public function getGpg(): OpenPGPBackend
    {
        return $this->gpg;
    }

    /**
     * @return \App\Model\Entity\User|null
     */
    public function getUser(): ?User
    {
        return $this->user;
    }

View on GitHub (pinned to 31c1bbc10f)