{"record":{"id":"57f1ceda5d4e50a9","repo":"roundcube/roundcubemail","slug":"failed-to-validate-jwt-missing-aud-azp-value","errorCode":null,"errorMessage":"Failed to validate JWT: missing aud/azp value","messagePattern":"Failed to validate JWT: missing aud/azp value","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"program/include/rcmail_oauth.php","lineNumber":452,"sourceCode":"            // Validate the token (throws exceptions)\n            $header = new \\stdClass();\n            $body = (array) JWT::decode($jwt, JWK::parseKeySet($this->jwks), $header);\n            $header = (array) $header;\n        } else {\n            [$headb64, $bodyb64, $cryptob64] = explode('.', $jwt);\n\n            $header = json_decode(static::base64url_decode($headb64), true);\n            $body = json_decode(static::base64url_decode($bodyb64), true);\n            // $crypto = static::base64url_decode($cryptob64);\n        }\n\n        // FIXME depends on body type: ID, Logout, Bearer, Refresh,\n        if (isset($body['azp']) && $body['azp'] !== $this->options['client_id']) {\n            throw new \\RuntimeException('Failed to validate JWT: invalid azp value');\n        } elseif (isset($body['aud']) && !in_array($this->options['client_id'], (array) $body['aud'])) {\n            throw new \\RuntimeException('Failed to validate JWT: invalid aud value');\n        } elseif (!isset($body['azp']) && !isset($body['aud'])) {\n            throw new \\RuntimeException('Failed to validate JWT: missing aud/azp value');\n        }\n\n        // if defined in parameters, check that issuer match\n        if (isset($this->options['issuer']) && $body['iss'] !== $this->options['issuer']) {\n            throw new \\RuntimeException('Failed to validate JWT: issuer mismatch');\n        }\n\n        // check that token is not an outdated message\n        if (isset($body['exp']) && (time() > $body['exp'])) {\n            throw new \\RuntimeException('Failed to validate JWT: expired message');\n        }\n\n        $body['header'] = $header;\n\n        $this->log_debug('jwt: %s', json_encode($body));\n\n        return $body;\n    }","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/roundcube/roundcubemail/blob/4b54c2acfb54d5ee3d1c281ca7f143bed0dea804/program/include/rcmail_oauth.php#L434-L470","documentation":"Every OIDC token must bind to a party: jwt_decode() requires at least one of 'azp' or 'aud'. A token with neither cannot be attributed to this client, so it is rejected.","triggerScenarios":"parse_tokens() -> jwt_decode() on a token whose decoded payload contains neither 'azp' nor 'aud' claims.","commonSituations":"Custom or minimal IDP issuing bare JWTs without audience claims; malformed test tokens; provider emitting opaque-style tokens as JWTs without standard claims; userinfo-like payloads mistakenly passed through jwt_decode.","solutions":["Configure the IDP to include 'aud' (or 'azp') in issued tokens.","Verify the token type reaching jwt_decode is an ID/access/logout token, not a raw userinfo payload.","Decode the token and confirm which claims exist; fix the issuing endpoint accordingly.","If you control a test issuer, add \"aud\":\"<client_id>\" to the payload."],"exampleFix":"// before\n{\"iss\":\"https://idp\",\"sub\":\"u1\",\"exp\":1893456000}\n// after\n{\"iss\":\"https://idp\",\"sub\":\"u1\",\"aud\":\"roundcube\",\"exp\":1893456000}","handlingStrategy":"validation","validationCode":"$claims = json_decode(base64_decode(strtr(explode('.', $token)[1], '-_', '+/')), true);\nif (!isset($claims['azp']) && !isset($claims['aud'])) { /* abort: unattributable token */ }","typeGuard":"function hasPartyClaim(array $claims): bool { return isset($claims['azp']) || isset($claims['aud']); }","tryCatchPattern":"try { $payload = $oauth->jwt_decode($token); } catch (\\RuntimeException $e) { // reject token, redirect to login }","preventionTips":["Require the IDP to always emit 'aud' on JWTs.","Validate token shape with a JWT library before sending to Roundcube.","Avoid routing non-standard payloads through jwt_decode."],"tags":["oauth","jwt","validation","oidc"],"backgroundTag":"missing-required-argument","analyzedSha":"4b54c2acfb54d5ee3d1c281ca7f143bed0dea804","analyzedAt":"2026-09-14T13:23:19.231Z","contentChangedAt":"2026-09-14T13:23:19.231Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}