Date claims before the Unix epoch are rounded toward zero
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
Research direction
Start by inspecting ClaimsSerializer.dateToSeconds, then run the reported Java reproduction with Date(-500) and the equivalent Instant. The fix is complete when both claims serialize to -1 for the negative timestamp while non-negative timestamps remain unchanged, with regression coverage for the mismatch.
Written by the indexing model from the issue text.
Description
Checklist
- I have looked into the README and Examples and have not found a suitable solution or answer.
- I have looked into the API documentation and have not found a suitable solution or answer.
- I have searched the issues and pull requests and have not found a duplicate.
- I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- I agree to the terms within the Auth0 Code of Conduct.
Description
Date claims before the Unix epoch are converted to seconds using integer division, which truncates toward zero. This makes a Date and an Instant representing the same point in time serialize to different NumericDate values.
For example, new Date(-500) is half a second before the epoch. Its whole epoch second is -1, which is also returned by date.toInstant().getEpochSecond(). However, the Date serializer currently writes 0 because -500 / 1000 == 0 in Java.
Expected: equivalent Date and Instant values serialize to the same epoch second (-1).
Actual: the Date serializes to 0, while the equivalent Instant serializes to -1.
Reproduction
Date date = new Date(-500);
Instant instant = date.toInstant();
String token = JWT.create()
.withClaim("date", date)
.withClaim("instant", instant)
.sign(Algorithm.none());
DecodedJWT decoded = JWT.decode(token);
System.out.println(decoded.getClaim("date").asLong()); // 0
System.out.println(decoded.getClaim("instant").asLong()); // -1
The inconsistency originates in ClaimsSerializer.dateToSeconds, which uses date.getTime() / 1000, while the Instant path uses Instant.getEpochSecond().
Additional context
Using floor division for millisecond-to-second conversion would make the Date path consistent with Instant for negative timestamps while leaving non-negative timestamps unchanged.
java-jwt version
4.6.1 (master at 29f252b)
Java version
Java 17.0.14
AI assistance disclosure: Codex assisted with identifying and structuring this report. The reproduction and analysis were reviewed and verified before submission.
- Dominant language
- Java
- Stars
- 6.2k
- Forks
- 945
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 4
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from auth0/java-jwt
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 42/100
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 72/100
-
feature request
Difficulty 4/5 3-5 days Newbie friendliness 45/100
-
feature request
Difficulty 3/5 1-2 days Newbie friendliness 45/100
-
feature request
Difficulty 3/5 1-2 days Newbie friendliness 35/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
infinispan/infinispan#18150 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
opensearch-project/k-NN#3597 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100