NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
JWT for Laravel

Exceptions

RoundlyConsulting\Jwt\Jose\Exceptions\JwtException extends RuntimeException and is the base of every token error. Catch it to handle any failure uniformly, or a subclass for a precise cause:

ExceptionThrown when
JwtExceptionBase class — catch it to handle any token failure.
MalformedTokenNot three segments, invalid base64url, a header or payload that isn’t a JSON object, or a token over 8 KB.
AlgorithmMismatchThe header alg isn’t the pinned algorithm, or the key type doesn’t match it — blocks alg:none and RS256↔HS256 confusion.
InvalidSignatureThe RS256 or HS256 signature fails; also every per-issuer service-token iss absent from the secret map, even an allow-listed one.
UnexpectedTokenTypeA typ header is present and isn’t JWT (RFC 8725 explicit typing).
UnencodableClaimsA claim can’t be serialised to JSON, e.g. non-UTF-8 bytes in an extra claim.
TokenExpiredexp, minus leeway, is in the past.
TokenNotYetValidnbf or iat is in the future beyond the leeway.
UnexpectedCriticalHeaderThe token carries a crit header.
ClaimMismatchA required claim is absent or mistyped; an iss/aud pin mismatch; service scope, audience or issuer checks.
KeyLoadFailedA key is missing, unreadable (the file exists but the PHP process can’t read it), not RSA, under 2048 bits, or its path isn’t configured.
EmptySecretAn HMAC secret is empty.
PemAsHmacSecretA PEM-encoded key was passed as an HMAC secret.
WeakSecretAn HMAC secret is under 32 bytes, a single repeated byte, or carries raw DER key material.
use RoundlyConsulting\Jwt\Facades\Jwt;
use RoundlyConsulting\Jwt\Jose\Exceptions\JwtException;
use RoundlyConsulting\Jwt\Jose\Exceptions\TokenExpired;

try {
    $claims = Jwt::verify($jwt);
} catch (TokenExpired $e) {
    // Prompt a re-login / refresh flow.
} catch (JwtException $e) {
    // Any other token failure: 401.
}

Misconfiguration is a 500, not a 401

Both guards turn a JwtException into a null user — a 401. Operator errors must never take that path, so they are deliberately not JwtExceptions:

  • RoundlyConsulting\Jwt\Exceptions\JwtMisconfigured — an issuer or audience that is not set (null or blank), a guard name that isn’t a jwt guard, an uncallable token_version, a guard mintAccessToken() whose request names another audience, or a config value of the wrong shape — a TTL of five, an unparseable switch, a malformed service.secrets pair or an issuer it names twice, an identity that isn’t a ClaimsAuthenticatable. It never prints a secret: a repeated issuer is named only when it looks like one, otherwise by the entries’ positions, and for service.secret / service.secrets the message gives only the value’s type (“must be a string or null, array given”), while the value never rides a trace frame.
  • RoundlyConsulting\Jwt\ServiceTokens\Exceptions\ServiceAuthMisconfigured — a missing or weak service secret, no resolvable service name, no audience or own issuer on issue, a missing own entry in the per-issuer map, or reserved extra claims.
  • KeyLoadFailed is a JwtException, but the jwt guard rethrows it ahead of the generic handler — a missing or invalid RSA key is an operator error, not an unauthenticated caller.

KeyLoadFailed messages are actionable: they name the missing or unreadable path and the fix, such as running jwt:generate-keys, setting the env var or granting the PHP user read access.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.