BookStackApp/BookStack · error · OidcInvalidTokenException
Token authorized party exists but does not match the expecte
Error message
Token authorized party exists but does not match the expected client_id
What it means
Per OIDC Core spec section 3.1.3.7, when an azp (authorized party) claim is present it must equal the client ID of the consuming client. OidcIdToken rejects the token when azp exists but does not match the configured client ID, because the token was authorized for a different party.
Source
Thrown at app/Access/Oidc/OidcIdToken.php:48
// Already done in parent.
// 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered
// at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected
// if the ID Token does not list the Client as a valid audience, or if it contains additional
// audiences not trusted by the Client.
// Partially done in parent.
$aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud'];
if (count($aud) !== 1) {
throw new OidcInvalidTokenException('Token audience value has ' . count($aud) . ' values, Expected 1');
}
// 3. If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present.
// NOTE: Addressed by enforcing a count of 1 above.
// 4. If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id
// is the Claim Value.
if (isset($this->payload['azp']) && $this->payload['azp'] !== $clientId) {
throw new OidcInvalidTokenException('Token authorized party exists but does not match the expected client_id');
}
// 5. The current time MUST be before the time represented by the exp Claim
// (possibly allowing for some small leeway to account for clock skew).
if (empty($this->payload['exp'])) {
throw new OidcInvalidTokenException('Missing token expiration time value');
}
$skewSeconds = 120;
$now = time();
if ($now >= (intval($this->payload['exp']) + $skewSeconds)) {
throw new OidcInvalidTokenException('Token has expired');
}
// 6. The iat Claim can be used to reject tokens that were issued too far away from the current time,
// limiting the amount of time that nonces need to be stored to prevent attacks.
// The acceptable range is Client specific.
if (empty($this->payload['iat'])) {View on GitHub (pinned to 18f8469a1c)
Solutions
- Compare the token's azp claim (decode the JWT) with the client_id configured in BookStack and make them match
- Fix the OIDC client_id in BookStack .env/config if it is wrong
- At the IdP, configure the client/mapper so azp equals the BookStack client ID
- Ensure tokens are being obtained through BookStack's own authorization flow, not reused from another application
Example fix
// before (.env) OIDC_CLIENT_ID=wrong-client // after OIDC_CLIENT_ID=bookstack-client # must equal the token's azp/aud
Defensive patterns
Strategy: validation
Validate before calling
$payload = /* decode jwt payload */;
if (isset($payload['azp']) && $payload['azp'] !== $expectedClientId) {
throw new InvalidArgumentException('azp does not match configured client_id');
} Type guard
function azpMatchesClient(array $payload, string $clientId): bool {
return !isset($payload['azp']) || $payload['azp'] === $clientId;
} Try / catch
try {
$idToken = OidcIdToken::validate($token, $clientId, $keys);
} catch (OidcInvalidTokenException $e) {
if (str_contains($e->getMessage(), 'authorized party')) {
Log::error('OIDC azp mismatch — check client_id config and IdP client setup');
}
throw $e;
} Prevention
- Ensure OIDC_CLIENT_ID exactly matches the IdP client the token was issued to
- Never reuse tokens issued to a different application
- After IdP realm/client renames, update BookStack config immediately
- Verify with a decoded token that azp equals your client_id
When it happens
Trigger: validateTokenClaims (via validate) sees payload['azp'] set to a value different from BookStack's configured OIDC client ID — the token was minted for another client or the IdP sets azp to a client identifier that differs from the aud value.
Common situations: Keycloak/Auth0 issuing tokens with azp from a different client due to token exchange or impersonation; BookStack client_id misconfigured (e.g. pointing at the wrong Keycloak client); copying a token from another application during debugging; IdP realm migration changing client IDs.
Related errors
- Token audience value has ' . count($aud) . ' values, Expecte
- Missing token expiration time value
- Missing token issued at time value
- Missing token subject value
- Missing token audience value
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/e56286c465951c4c.
Report an issue: GitHub.