{"record":{"id":"2abc821ea9944636","repo":"apereo/cas","slug":"jwt-time-claim-is-invalid","errorCode":null,"errorMessage":"JWT time claim is invalid","messagePattern":"JWT time claim is invalid","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/vc/presentation/OidcVerifiableCredentialPresentationResponseEndpointController.java","lineNumber":451,"sourceCode":"            \"Key binding JWT predates the presentation transaction\");\n    }\n\n    private static @Nullable Instant readNumericDate(final Map<String, Object> claims,\n                                                     final String name,\n                                                     final boolean required) {\n        val value = claims.get(name);\n        require(value != null || !required, \"JWT is missing a required time claim\");\n        if (value == null) {\n            return null;\n        }\n        require(value instanceof Number, \"JWT time claim is invalid\");\n        try {\n            val numericDate = new BigDecimal(value.toString());\n            val components = numericDate.divideAndRemainder(BigDecimal.ONE);\n            return Instant.ofEpochSecond(components[0].longValueExact(),\n                components[1].movePointRight(9).longValueExact());\n        } catch (final ArithmeticException exception) {\n            throw new IllegalArgumentException(\"JWT time claim is invalid\", exception);\n        }\n    }\n\n    private static List<String> readAudience(final Map<String, Object> claims) {\n        val audience = claims.get(\"aud\");\n        if (audience instanceof final String value) {\n            return List.of(value);\n        }\n        if (audience instanceof final List<?> values\n            && values.stream().allMatch(String.class::isInstance)) {\n            return values.stream().map(String.class::cast).toList();\n        }\n        throw new IllegalArgumentException(\"JWT audience is invalid\");\n    }\n\n    private static String requiredStringClaim(final Map<String, Object> claims, final String name) {\n        val value = claims.get(name);\n        if (!(value instanceof final String stringValue) || stringValue.isBlank()) {","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/vc/presentation/OidcVerifiableCredentialPresentationResponseEndpointController.java#L433-L469","documentation":"readNumericDate converts a JWT time claim (exp/nbf/iat) to an Instant via BigDecimal epoch-second parsing, including nanoseconds. A non-numeric or numerically infeasible value raises ArithmeticException, which is rethrown as IllegalArgumentException(\"JWT time claim is invalid\").","triggerScenarios":"A time claim whose string representation cannot be converted to epoch seconds — e.g. a non-numeric string like 'now', a value exceeding long range, or a fractional part with too many digits for longValueExact/movePointRight conversion.","commonSituations":"Clients placing formatted dates ('2026-01-01T00:00:00Z') instead of NumericDate epoch seconds in exp/nbf/iat; keys generating oversized numeric claims; corrupted JWT payloads.","solutions":["Fix the JWT producer to emit NumericDate epoch seconds (possibly fractional) for time claims","Validate the claim value on the client before signing","Decode the token and inspect the raw exp/nbf/iat values with a JWT debugger"],"exampleFix":"// before\nclaims.setExpiration(new Date(\"2026-09-08\")); // formatted date, not NumericDate\n// after\nclaims.setExpiration(new Date(System.currentTimeMillis() + 3600_000L)); // epoch millis -> NumericDate","handlingStrategy":"validation","validationCode":"Object v = claims.get(\"exp\");\nboolean ok = (v instanceof Number) || (v instanceof String s && s.matches(\"-?\\\\d+(\\\\.\\\\d+)?\"));","typeGuard":null,"tryCatchPattern":"try { Instant exp = controllerReadNumericDate(claims); }\ncatch (IllegalArgumentException e) { logger.warn(\"Invalid time claim format\"); rejectToken(); }","preventionTips":["Always emit NumericDate (epoch seconds) for exp/nbf/iat","Unit-test JWT builders for claim types","Validate token payload shapes in CI"],"tags":["jwt","claims","time","format"],"backgroundTag":"invalid-argument-format","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}