tymondesigns/jwt-auth · error · Tymon\JWTAuth\Exceptions\TokenInvalidException

Malformed token

Error message

Malformed token

What it means

Thrown by TokenValidator::validateStructure() when the string has three dot-separated segments but they are not clean non-empty base64url bodies: after trim() and removal of empty segments the count changes, or rejoining the trimmed parts no longer reproduces the original. Concretely, at least one segment is empty (e.g. 'header..signature') or contains whitespace/line breaks inside it.

Source

Thrown at src/Validators/TokenValidator.php:46

    /**
     * @param  string  $token
     * @return string
     *
     * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
     */
    protected function validateStructure($token)
    {
        $parts = explode('.', $token);

        if (count($parts) !== 3) {
            throw new TokenInvalidException('Wrong number of segments');
        }

        $parts = array_filter(array_map('trim', $parts));

        if (count($parts) !== 3 || implode('.', $parts) !== $token) {
            throw new TokenInvalidException('Malformed token');
        }

        return $token;
    }
}

View on GitHub (pinned to 6c70930a92)

Solutions

  1. Log the received token wrapped in delimiters (e.g. "[$token]") to reveal invisible whitespace or embedded newlines
  2. Strip surrounding whitespace on the server before parsing if the transport adds it: $token = preg_replace('/\s+/', '', $token) - but fix the producer if whitespace appears inside the token
  3. Regenerate/re-login to obtain a clean token if the stored one was wrapped or truncated
  4. If you control the client, ensure the token is transmitted as a single line with no formatting applied (no word-wrap, no pretty-printing in storage)

Example fix

// before - token wrapped across lines in transit
"eyJhbGciOi...\neyJzdWIi...\n.E-mSig"  // TokenInvalidException: Malformed token

// after - normalize at the boundary, then parse
$token = preg_replace('/\s+/', '', $request->bearerToken());
$user = JWTAuth::parseToken()->authenticate();
Defensive patterns

Strategy: type-guard

Validate before calling

// Same strict shape check also rejects empty segments and embedded whitespace
function looksLikeJwt(?string $token): bool
{
    return is_string($token)
        && preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $token) === 1;
}

// optionally normalize transport noise first
$token = preg_replace('/\s+/', '', (string) $request->bearerToken());

Type guard

function looksLikeJwt(?string $token): bool
{
    return is_string($token)
        && preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $token) === 1;
}

Try / catch

use Tymon\JWTAuth\Exceptions\TokenInvalidException;

try {
    $user = auth('api')->parseToken()->authenticate();
} catch (TokenInvalidException $e) {
    // 'Malformed token' = structurally broken input; 401 and let the client re-authenticate
    return response()->json(['error' => 'token_malformed'], 401);
}

Prevention

When it happens

Trigger: Passing a token where the payload or signature segment is empty, or where any segment carries leading/trailing spaces, tabs, or embedded newlines - e.g. a token wrapped across lines by an email client, a header value folded by a proxy, or a copy-paste that introduced a space. Caught in TokenValidator::check, reached from new Token($value) inside JWTAuth::parseToken()->authenticate().

Common situations: Token truncated or wrapped to multiple lines before being sent; whitespace sneaking in through copy-paste or template interpolation; a client sending a two-part unsigned JWT ('header.payload.') which has an empty signature segment; middleware or proxies re-encoding the Authorization header; a token stored in a database TEXT column with a trailing newline.

Understand the failure class

Related errors


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