{"record":{"id":"2fa3a5e5bc6a3e30","repo":"BookStackApp/BookStack","slug":"missing-token-audience-value","errorCode":null,"errorMessage":"Missing token audience value","messagePattern":"Missing token audience value","errorType":"exception","errorClass":"OidcInvalidTokenException","httpStatus":null,"severity":"critical","filePath":"app/Access/Oidc/OidcJwtWithClaims.php","lineNumber":164,"sourceCode":"     * Validate common claims for OIDC JWT tokens.\n     * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation\n     * and https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse\n     *\n     * @throws OidcInvalidTokenException\n     */\n    protected function validateCommonClaims(string $clientId): void\n    {\n        // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)\n        // MUST exactly match the value of the iss (issuer) Claim.\n        if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) {\n            throw new OidcInvalidTokenException('Missing or non-matching token issuer value');\n        }\n\n        // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered\n        // at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected\n        // if the ID Token does not list the Client as a valid audience.\n        if (empty($this->payload['aud'])) {\n            throw new OidcInvalidTokenException('Missing token audience value');\n        }\n\n        $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud'];\n        if (!in_array($clientId, $aud, true)) {\n            throw new OidcInvalidTokenException('Token audience value did not match the expected client_id');\n        }\n    }\n}\n","sourceCodeStart":146,"sourceCodeEnd":173,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/Oidc/OidcJwtWithClaims.php#L146-L173","documentation":"OIDC requires every ID token to carry an aud (audience) claim listing the client(s) the token was issued for. If the payload's aud claim is absent or empty, validateCommonClaims throws OidcInvalidTokenException('Missing token audience value'). Without aud there is no way to confirm the token was issued for your client.","triggerScenarios":"validateCommonClaims($clientId) checks empty($this->payload['aud']) immediately after the issuer check. Triggers when: the IdP is misconfigured not to emit aud; the token is an access token (rather than an ID token) that omits aud; claim-mapping/mangling in middleware strips or renames aud; the token payload was decoded into $this->payload incorrectly (e.g. wrong key order or partially decoded).","commonSituations":"Feeding an opaque access token or userinfo response into an ID-token validator; IdP client configured without the audience mapping; custom claim filtering (e.g. a proxy that trims claims) removing aud; using a token type whose aud is named differently by the provider.","solutions":["Verify you are validating an ID token, not an access token — only ID tokens are guaranteed an aud claim for OIDC flows","Check the decoded payload (base64url-decode the second segment) to confirm aud is actually present; if missing, fix the token source, not the validator","In your IdP client settings, ensure the audience/client-id mapping is set so aud is always emitted","If an intermediary rewrites the payload (claims trimming, mapping), whitelist aud so it survives"],"exampleFix":"// before: validating an access token\n$token = $result->getAccessToken();\n$validator->validateCommonTokenDetails($token, $clientId);\n// after: validate the ID token, which carries aud\n$validator->validateCommonTokenDetails($result->getIdToken(), $clientId);","handlingStrategy":"validation","validationCode":"$payload = json_decode(base64_decode(strtr(explode('.', $token)[1], '-_', '+/') . '=='), true);\nif (empty($payload['aud'])) {\n    throw new UnexpectedValueException('Token has no aud claim — is this an ID token?');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $jwt->validateCommonTokenDetails($token, $clientId);\n} catch (OidcInvalidTokenException $e) {\n    if ($e->getMessage() === 'Missing token audience value') {\n        throw new UnauthorizedException('ID token lacks aud claim — ensure you are validating an ID token, not an access token');\n    }\n    throw $e;\n}","preventionTips":["Only pass ID tokens (from the id_token field of the auth response) into this validator; access tokens go to resource servers","Sanity-check the decoded payload's claim set once per provider integration (id tokens must have iss, sub, aud, exp, iat)","Audit any middleware that filters/maps claims to ensure aud is preserved","Enable IdP-side logging to confirm aud is emitted for your client"],"tags":["php","oidc","jwt","audience-validation","token-validation"],"backgroundTag":"jwt-aud-missing","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}