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

  1. 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
  2. If you mint tokens yourself, build payloads through this package's factory (auth claims / JWTAuth::fromSubject) so all required claims are always included
  3. 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
  4. If third-party tokens must verify here, align required_claims with what that issuer actually provides, keeping the list as small as security allows
  5. 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

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


AI-assisted analysis of tymondesigns/jwt-auth@6c70930a92 (2026-08-21). Data as JSON: /api/errors/e2aaaa662fe150e9. Report an issue: GitHub.