tymondesigns/jwt-auth · error · Tymon\JWTAuth\Exceptions\TokenInvalidException
JWT payload does not contain the required claims
Error message
JWT payload does not contain the required claims
What it means
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().
Source
Thrown at src/Validators/PayloadValidator.php:65
{
$this->validateStructure($value);
return $this->refreshFlow ? $this->validateRefresh($value) : $this->validatePayload($value);
}
/**
* Ensure the payload contains the required claims and
* the claims have the relevant type.
*
* @param \Tymon\JWTAuth\Claims\Collection $claims
* @return void
*
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
*/
protected function validateStructure(Collection $claims)
{
if ($this->requiredClaims && ! $claims->hasAllClaims($this->requiredClaims)) {
throw new TokenInvalidException('JWT payload does not contain the required claims');
}
}
/**
* Validate the payload timestamps.
*
* @param \Tymon\JWTAuth\Claims\Collection $claims
* @return \Tymon\JWTAuth\Claims\Collection
*
* @throws \Tymon\JWTAuth\Exceptions\TokenExpiredException
* @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
*/
protected function validatePayload(Collection $claims)
{
return $claims->validate('payload');
}
/**View on GitHub (pinned to 6c70930a92)
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
Example fix
// before - TTL disabled but exp still required 'ttl' => null, 'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'], // -> TokenInvalidException: JWT payload does not contain the required claims // after - as recommended by config/config.php docs 'ttl' => null, 'required_claims' => ['iss', 'iat', 'nbf', 'sub', 'jti'],
Defensive patterns
Strategy: try-catch
Validate before calling
// If you issue tokens yourself, assert the payload satisfies required claims before encoding
$required = config('jwt.required_claims', ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti']);
$missing = array_diff($required, array_keys($payload));
if ($missing !== []) {
throw new RuntimeException('Payload missing required claims: '.implode(', ', $missing));
} Type guard
function payloadHasRequiredClaims(array $claims, array $required = ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti']): bool
{
return array_diff($required, array_keys($claims)) === [];
} Try / catch
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
try {
$user = auth('api')->parseToken()->authenticate();
} catch (TokenInvalidException $e) {
// 'JWT payload does not contain the required claims' - reject as 401, token is unusable here
return response()->json(['error' => 'token_missing_claims'], 401);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Could not create token: {exception message}
- Token Signature could not be verified.
- The given algorithm could not be found
- Private key is not set.
- Secret is not set.
AI-assisted analysis of tymondesigns/jwt-auth@6c70930a92 (2026-08-21).
Data as JSON: /api/errors/e2aaaa662fe150e9.
Report an issue: GitHub.