lcobucci/jwt · error · ConstraintViolation

Token signature mismatch

Error message

Token signature mismatch

What it means

SignedWith is a validation constraint that verifies a token's signature with a specific signer and key. It first checks that the token's 'alg' header matches the signer's algorithm, throwing 'Token signer mismatch' if not, then verifies the cryptographic signature. 'Token signature mismatch' means the alg header matched but the signature could not be verified with the given key.

Solutions

  1. Verify you pass the exact same key (or correct public key for asymmetric algorithms) used to sign the token
  2. Confirm the token was not modified in transit — copy it whole without added whitespace or newlines
  3. Check that signer algorithm matches how the token was actually signed (e.g. new Sha256() vs Sha384())
  4. Log/compare the token's alg header and your signer's algorithmId() to rule out algorithm drift
  5. Regenerate the token with the current key to confirm the verification path itself works

Example fix

// before
$validator->assert($token, new SignedWith(new Sha256(), InMemory::plainText('wrong-secret')));
// after
$validator->assert($token, new SignedWith(new Sha256(), InMemory::plainText('the-same-secret-used-to-sign')));
Defensive patterns

Strategy: try-catch

Validate before calling

if ($token instanceof UnencryptedToken && $token->headers()->get('alg') === 'HS256') {
    // ensure your key material matches the one used at signing time before asserting
}

Type guard

function isPlainSignedToken($token): bool { return $token instanceof UnencryptedToken && $token->signature()->hash() !== ''; }

Try / catch

try {
    $validator->assert($token, new SignedWith(new Sha256(), $key));
} catch (ConstraintViolation $e) {
    // reject request / force re-authentication; log $e->getMessage()
}

Prevention

When it happens

Trigger: Calling Validator::assert($token, new SignedWith($signer, $key)) where the token's signature does not verify against the provided key — e.g. signed with a different private key, key rotated since issuance, or token payload was modified after signing.

Common situations: Using a verification key that differs from the signing key; testing tokens from another environment (staging token verified in prod); key rotation without token invalidation handling; tampered or truncated tokens copied incorrectly (extra whitespace/quotes); HS256 secret mismatch between services.

Related errors


AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14). Data as JSON: /api/errors/128ece85179dcd77. Report an issue: GitHub.

Appendix: source

Thrown at src/Validation/Constraint/SignedWith.php:29

final readonly class SignedWith implements SignedWithInterface
{
    public function __construct(private Signer $signer, private Signer\Key $key)
    {
    }

    public function assert(Token $token): void
    {
        if (! $token instanceof UnencryptedToken) {
            throw ConstraintViolation::error('You should pass a plain token', $this);
        }

        if ($token->headers()->get('alg') !== $this->signer->algorithmId()) {
            throw ConstraintViolation::error('Token signer mismatch', $this);
        }

        if (! $this->signer->verify($token->signature()->hash(), $token->payload(), $this->key)) {
            throw ConstraintViolation::error('Token signature mismatch', $this);
        }
    }
}

View on GitHub (pinned to 375813049c)