{"record":{"id":"b538e208de6f1861","repo":"lcobucci/jwt","slug":"you-should-pass-a-plain-token-hasclaimwithvalue","errorCode":null,"errorMessage":"You should pass a plain token","messagePattern":"You should pass a plain token","errorType":"exception","errorClass":"Lcobucci\\JWT\\Validation\\ConstraintViolation","httpStatus":null,"severity":"error","filePath":"src/Validation/Constraint/HasClaimWithValue.php","lineNumber":26,"sourceCode":"use Lcobucci\\JWT\\Validation\\Constraint;\nuse Lcobucci\\JWT\\Validation\\ConstraintViolation;\n\nuse function in_array;\n\nfinal readonly class HasClaimWithValue implements Constraint\n{\n    /** @param non-empty-string $claim */\n    public function __construct(private string $claim, private mixed $expectedValue)\n    {\n        if (in_array($claim, Token\\RegisteredClaims::ALL, true)) {\n            throw CannotValidateARegisteredClaim::create($claim);\n        }\n    }\n\n    public function assert(Token $token): void\n    {\n        if (! $token instanceof UnencryptedToken) {\n            throw ConstraintViolation::error('You should pass a plain token', $this);\n        }\n\n        $claims = $token->claims();\n\n        if (! $claims->has($this->claim)) {\n            throw ConstraintViolation::error('The token does not have the claim \"' . $this->claim . '\"', $this);\n        }\n\n        if ($claims->get($this->claim) !== $this->expectedValue) {\n            throw ConstraintViolation::error(\n                'The claim \"' . $this->claim . '\" does not have the expected value',\n                $this,\n            );\n        }\n    }\n}\n","sourceCodeStart":8,"sourceCodeEnd":43,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Validation/Constraint/HasClaimWithValue.php#L8-L43","documentation":"HasClaimWithValue requires an UnencryptedToken to read its claim set. Passing any other Token type to assert() throws ConstraintViolation 'You should pass a plain token'. Encrypted/opaque tokens cannot have their claims inspected.","triggerScenarios":"(new HasClaimWithValue('role', 'admin'))->assert($token) where $token is not an instance of UnencryptedToken (e.g. an encrypted JWE token).","commonSituations":"Running the same validator over encrypted and plain tokens; a decryption step failing silently upstream so an opaque token reaches the validator; passing the wrong variable to assert().","solutions":["Decrypt the token (or obtain the plain signed token) before validating claims","Guard with instanceof UnencryptedToken before running the constraint","Separate validation flows for encrypted vs plain tokens"],"exampleFix":"// before\n$constraint->assert($jweToken);\n// after\nif ($token instanceof UnencryptedToken) {\n    $constraint->assert($token);\n}","handlingStrategy":"type-guard","validationCode":"if (!$token instanceof \\Jose\\Component\\Core\\UnencryptedToken) {\n    throw new \\LogicException('Value constraint needs a plain token');\n}","typeGuard":"function isPlainToken(\\Jose\\Component\\Core\\TokenInterface $t): bool {\n    return $t instanceof \\Jose\\Component\\Core\\UnencryptedToken;\n}","tryCatchPattern":"try {\n    $validator->assert($token, new HasClaimWithValue('role', 'admin'));\n} catch (ConstraintViolationException $e) {\n    // non-plain token or value mismatch; reject\n}","preventionTips":["Decrypt before claim validation","Assert token type before building the validator pipeline","Document that claim constraints apply only to JWS tokens"],"tags":["jwt","validation","constraint","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"375813049c24c7111bda8b6884c57b071ceb2fe7","analyzedAt":"2026-09-14T11:12:28.004Z","contentChangedAt":"2026-09-14T11:12:28.004Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}