{"record":{"id":"e2aaaa662fe150e9","repo":"tymondesigns/jwt-auth","slug":"jwt-payload-does-not-contain-the-required-claims","errorCode":null,"errorMessage":"JWT payload does not contain the required claims","messagePattern":"JWT payload does not contain the required claims","errorType":"exception","errorClass":"Tymon\\JWTAuth\\Exceptions\\TokenInvalidException","httpStatus":null,"severity":"error","filePath":"src/Validators/PayloadValidator.php","lineNumber":65,"sourceCode":"    {\n        $this->validateStructure($value);\n\n        return $this->refreshFlow ? $this->validateRefresh($value) : $this->validatePayload($value);\n    }\n\n    /**\n     * Ensure the payload contains the required claims and\n     * the claims have the relevant type.\n     *\n     * @param  \\Tymon\\JWTAuth\\Claims\\Collection  $claims\n     * @return void\n     *\n     * @throws \\Tymon\\JWTAuth\\Exceptions\\TokenInvalidException\n     */\n    protected function validateStructure(Collection $claims)\n    {\n        if ($this->requiredClaims && ! $claims->hasAllClaims($this->requiredClaims)) {\n            throw new TokenInvalidException('JWT payload does not contain the required claims');\n        }\n    }\n\n    /**\n     * Validate the payload timestamps.\n     *\n     * @param  \\Tymon\\JWTAuth\\Claims\\Collection  $claims\n     * @return \\Tymon\\JWTAuth\\Claims\\Collection\n     *\n     * @throws \\Tymon\\JWTAuth\\Exceptions\\TokenExpiredException\n     * @throws \\Tymon\\JWTAuth\\Exceptions\\TokenInvalidException\n     */\n    protected function validatePayload(Collection $claims)\n    {\n        return $claims->validate('payload');\n    }\n\n    /**","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/tymondesigns/jwt-auth/blob/6c70930a92710d97e8e52b182fca2176097f33be/src/Validators/PayloadValidator.php#L47-L83","documentation":"Thrown by PayloadValidator::validateStructure() when the decoded payload's claim collection is missing one or more of the configured required claims (defaults: iss, iat, exp, nbf, sub, jti). It runs after signature verification, so the token is authentic but was issued without the claims this app demands. The required set is configurable via the 'required_claims' config key or PayloadValidator::setRequiredClaims().","triggerScenarios":"auth('api')->user(), JWTAuth::parseToken()->authenticate(), or JWTAuth::check()/setPayload on a token whose payload omits a default required claim - typically tokens minted by another service or a custom encoder that skipped claims (e.g. no jti, no nbf), or when 'ttl' is set to null so the factory stops adding exp while 'exp' is still required.","commonSituations":"Setting JWT_TTL=null (documented in config/config.php) but forgetting to remove 'exp' from required_claims; verifying third-party-issued JWTs against this package; a required_claims list customized in config/jwt.php that the issuing side does not satisfy; tokens from an older version of the library with different defaults.","solutions":["Decode the token's payload manually (list($h, $p) = explode('.', $token); json_decode(base64_decode(strtr($p, '-_', '+/')))) and diff its keys against your required_claims to see exactly which claim is absent","If you mint tokens yourself, build payloads through this package's factory (auth claims / JWTAuth::fromSubject) so all required claims are always included","If the absent claim is intentionally omitted (e.g. exp with JWT_TTL=null), remove it from the required_claims array in config/jwt.php","If third-party tokens must verify here, align required_claims with what that issuer actually provides, keeping the list as small as security allows","Run php artisan config:clear after changing required_claims"],"exampleFix":"// before - TTL disabled but exp still required\n'ttl' => null,\n'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'],\n// -> TokenInvalidException: JWT payload does not contain the required claims\n\n// after - as recommended by config/config.php docs\n'ttl' => null,\n'required_claims' => ['iss', 'iat', 'nbf', 'sub', 'jti'],","handlingStrategy":"try-catch","validationCode":"// If you issue tokens yourself, assert the payload satisfies required claims before encoding\n$required = config('jwt.required_claims', ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti']);\n$missing = array_diff($required, array_keys($payload));\nif ($missing !== []) {\n    throw new RuntimeException('Payload missing required claims: '.implode(', ', $missing));\n}","typeGuard":"function payloadHasRequiredClaims(array $claims, array $required = ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti']): bool\n{\n    return array_diff($required, array_keys($claims)) === [];\n}","tryCatchPattern":"use Tymon\\JWTAuth\\Exceptions\\TokenInvalidException;\n\ntry {\n    $user = auth('api')->parseToken()->authenticate();\n} catch (TokenInvalidException $e) {\n    // 'JWT payload does not contain the required claims' - reject as 401, token is unusable here\n    return response()->json(['error' => 'token_missing_claims'], 401);\n}","preventionTips":["Mint tokens through this package's factory/claims flow so required claims are always present by construction","Whenever you set 'ttl' => null, immediately remove 'exp' from required_claims (the config file documents exactly this trap)","If verifying third-party tokens, align required_claims with the issuer's actual claim set and keep the list minimal","Include an issue-then-authenticate round-trip test to catch required-claim drift between issuer and validator"],"tags":["jwt","php","laravel","claims","payload-validation","configuration"],"backgroundTag":"jwt-missing-required-claims","analyzedSha":"6c70930a92710d97e8e52b182fca2176097f33be","analyzedAt":"2026-08-21T02:16:37.040Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}