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
- 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
- Make the client use exactly the same base URL as fullBaseUrl, including scheme and port
- Check reverse-proxy headers (X-Forwarded-Proto/Host) so Router::url('/', true) resolves to the public URL
- 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
- Always set App.fullBaseUrl explicitly to the public URL
- Never switch scheme/host behind a proxy without updating fullBaseUrl
- Test login after any domain/TLS change
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
- Could not import the user OpenPGP key.
- The OpenPGP server key defined in the config cannot be used…
- The user signature could not be verified.
- You need to login to access this location.
- Attempt to access an expired verify token.
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)