{"record":{"id":"c0b7fb3b1e6d9926","repo":"lcobucci/jwt","slug":"you-should-pass-a-plain-token-strictvalidat","errorCode":null,"errorMessage":"You should pass a plain token","messagePattern":"You should pass a plain token","errorType":"validation","errorClass":"ConstraintViolation","httpStatus":null,"severity":"error","filePath":"src/Validation/Constraint/StrictValidAt.php","lineNumber":39,"sourceCode":"    }\n\n    private function guardLeeway(?DateInterval $leeway): DateInterval\n    {\n        if ($leeway === null) {\n            return new DateInterval('PT0S');\n        }\n\n        if ($leeway->invert === 1) {\n            throw LeewayCannotBeNegative::create();\n        }\n\n        return $leeway;\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        $now = $this->clock->now();\n\n        $this->assertIssueTime($token, $now->add($this->leeway));\n        $this->assertMinimumTime($token, $now->add($this->leeway));\n        $this->assertExpiration($token, $now->sub($this->leeway));\n    }\n\n    /** @throws ConstraintViolation */\n    private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void\n    {\n        if (! $token->claims()->has(Token\\RegisteredClaims::EXPIRATION_TIME)) {\n            throw ConstraintViolation::error('\"Expiration Time\" claim missing', $this);\n        }\n\n        if ($token->isExpired($now)) {\n            throw ConstraintViolation::error('The token is expired', $this);","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Validation/Constraint/StrictValidAt.php#L21-L57","documentation":"StrictValidAt requires an UnencryptedToken because it must read the plain (unencrypted) claim set to check iat, nbf, and exp. When a Token (e.g. an encrypted JWE) is passed instead of an UnencryptedToken (a decrypted JWS), it throws ConstraintViolation 'You should pass a plain token'. You must decrypt first, then validate time claims on the decrypted result.","triggerScenarios":"Passing an encrypted token object (Token) rather than UnencryptedToken to Validator::assert() with a StrictValidAt constraint — typically when using the JWE support (encrypt/decrypt API) and validating the encrypted container instead of the decrypted payload.","commonSituations":"Token stored/transmitted as JWE and fed directly into a validator configured with time constraints; refactored code that switched from signed to encrypted tokens without adding a decryption step; confusion between NonEncryptedToken types after loading via token string parsing.","solutions":["Decrypt the token first with the Encryption/decrypt API, then validate the resulting UnencryptedToken","Ensure you construct constraints for the JWS (decrypted) token, not the JWE container","Check that your token parsing produces a PlainToken/SignedToken (UnencryptedToken) before validation"],"exampleFix":"// before\n$validator->assert($encryptedToken, new StrictValidAt($clock));\n// after\n$decrypted = $encryption->decrypt($encryptedToken);\n$validator->assert($decrypted, new StrictValidAt($clock));","handlingStrategy":"type-guard","validationCode":"if (! $token instanceof UnencryptedToken) { throw new InvalidArgumentException('Decrypt token before strict time validation'); }","typeGuard":"function isPlainToken(Token $token): bool { return $token instanceof UnencryptedToken; }","tryCatchPattern":"try {\n    $validator->assert($token, new StrictValidAt($clock));\n} catch (ConstraintViolation $e) {\n    if (str_contains($e->getMessage(), 'plain token')) { $token = $encryption->decrypt($token); }\n}","preventionTips":["Always decrypt JWE tokens before running claim-based constraints","Type-hint UnencryptedToken in validation entry points so violations surface early","Separate your validation pipeline into decrypt-then-validate stages"],"tags":["jwt","type-mismatch","encryption","validation"],"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"}