JWTParams.exp was typed str and filled with an ISO-8601 string, copied
verbatim into the reserved RFC 7519 exp claim. PyJWT requires exp to be a
NumericDate and raised DecodeError (subclass of PyJWTError), which the
blanket handler turned into 'Invalid JWT' — so every token minted with
--expires or expires_at failed auth. The module's own ISO comparison was
dead code because decode always failed first.
- JWTParams.exp: datetime | None; create_jwt unchanged (PyJWT converts)
- verify_jwt: drop the dead ISO comparison; catch jwt.ExpiredSignatureError
before PyJWTError so expired tokens report 'JWT expired'
- keys.py: pass expires_at through directly; drop unused format import
- generate_jwt.py: keep ISO string for display, datetime for the claim
- Tests: TestJWTExpiry (future/past/ISO-string/tampered) + rewritten
test_create_key_with_expires_at verifying minted keys both ways
Signed-off-by: ChethanUK <chethanuk@outlook.com>