Skip to content

Conversation

damnmicrowave
Copy link

@damnmicrowave damnmicrowave commented Apr 9, 2024

Technically, jose supports passing timey strings, e.g. '1h', '10days' etc. (here)
This PR updates the type of exp to be string as well as a number for that.

Example:

const token = await jwt.sign({
  userId: 'qwbfo21b9d',
  exp: '1h', // <~ this now works fine. Previously this was ignored and threw a type error
})

Summary by CodeRabbit

  • New Features
    • Added support for JWT payloads where the expiration (exp) claim can be provided as either a number or a string.
    • Improves compatibility with tokens generated by various issuers and environments that encode exp as a string, reducing integration friction without impacting existing number-based usage.

@fallow64
Copy link

fallow64 commented Jul 9, 2024

I was so confused why my exp field was not being passed into my token; this would be a great QOL feature, and certainly would save others from hours of debugging.

@pichxyaponn
Copy link
Contributor

Hi @damnmicrowave,

Thanks a lot for opening this PR and pointing out the issue regarding dynamic exp configuration in jwt.sign(). I completely agree with both you and @fallow64 that this has been a point of confusion, and resolving it would indeed be a significant quality-of-life improvement for usability.

I've forked this library and have been working on some enhancements, including a fix for this particular issue, in my version. I wanted to share it in case it might be helpful.

In my version (@pichxyaponn/elysia-jwt), the jwt.sign() method now correctly processes exp and nbf values passed directly in the payload, allowing dynamic overrides of exp and nft. Additionally, I've updated the jwt.verify() method has been refactored to throw errors when it is token expired, instead of returning false, which allows for more granular error handling.

Given that the main repository may not have been updated for a while, my fork could serve as an alternative for anyone looking for these features or facing the same issues. I'm also open to feedback and further suggestions on my version.

If you're interested, you can find more details here:

Copy link

coderabbitai bot commented Sep 30, 2025

Walkthrough

The JWT payload interface JWTPayloadSpec in src/index.ts was updated to allow the exp claim to be a number or a string, changing its type from number to number | string. No other files or logic were modified.

Changes

Cohort / File(s) Change Summary
JWT payload typing
src/index.ts
Widened JWTPayloadSpec.exp type from number to number | string; no control-flow or logic changes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I nudge the claims with a gentle tap,
Now exp can string or number map.
A hop, a skip, no logic bend—
Just types that flex, and backward-friend.
Thump-thump! My paws approve the spec—
More carrots for the typing check. 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the primary change of widening the exp type and enabling its override in jwt.sign(), matching the PR’s focus on both adjusting the exp type and fixing default value behavior.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1ef884 and 951fe2c.

📒 Files selected for processing (1)
  • src/index.ts (1 hunks)

* @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4 RFC7519#section-4.1.4}
*/
exp?: number
exp?: number | string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Critical: Type/validator mismatch will cause runtime validation failures.

The TypeScript type now allows exp to be number | string, but the validator schema at line 221 only accepts t.Number(). When users pass a string value for exp and schema validation is enabled, the validator will reject it at runtime, causing validation errors.

Additionally, per RFC 7519 section 4.1.4, the exp claim in the final JWT payload must be a NumericDate (numeric value representing seconds since epoch). While jose's setExpirationTime() accepts time-span strings like '1h' as input for convenience, it converts them to numeric timestamps before encoding them into the JWT.

Since JWTPayloadSpec represents the structure of the JWT payload (as stated in its JSDoc comment at lines 39-46), it should match the RFC 7519 specification where exp is always numeric. The JWTPayloadInput interface (line 122) correctly allows string | number for input flexibility.

Recommendation:

  1. Revert JWTPayloadSpec.exp back to number only (RFC-compliant)
  2. Keep JWTPayloadInput.exp as string | number (already correct at line 122)
  3. The existing implementation at lines 350-352 is correct and doesn't need changes

This maintains the distinction between input types (flexible) and payload types (RFC-compliant).

-	exp?: number | string
+	exp?: number

Run the following script to verify how jose handles string exp values and confirm the validator mismatch:

Additionally, please search for the RFC 7519 specification on the exp claim type:


🏁 Script executed:

#!/bin/bash
# Verify the validator schema for exp claim
echo "=== Validator schema for exp (should be t.Number()) ==="
rg -n "exp.*t\.(Optional|Number)" src/index.ts

echo ""
echo "=== All references to JWTPayloadSpec ==="
rg -n "JWTPayloadSpec" --type ts

echo ""
echo "=== Check if setExpirationTime is called with exp ==="
rg -n -A2 "setExpirationTime" src/index.ts

Length of output: 665


🌐 Web query:

RFC 7519 JWT exp claim type specification NumericDate

💡 Result:

Short answer — what NumericDate is and how exp uses it:

  • NumericDate = number of seconds (not milliseconds) since 1970-01-01T00:00:00Z UTC, ignoring leap seconds; it may be an integer or a non‑integer (fractional seconds). [1]
  • The "exp" claim’s value MUST be a number containing a NumericDate. The JWT MUST NOT be accepted if the current time is at or after that NumericDate; implementations MAY allow a small clock‑skew leeway (usually a few minutes). [1][2]

Sources:
[1] RFC 7519 (NumericDate definition and "exp" processing).
[2] Practical clarification (e.g., community/implementation notes confirming seconds, not milliseconds).


Revert JWTPayloadSpec.exp to number only (align with validator & RFC-compliant)

The interface in src/index.ts:89 was widened to number | string, but the runtime schema (line 221: t.Optional(t.Number())) only accepts numbers and RFC 7519 mandates exp be a NumericDate number. Keep JWTPayloadInput.exp as string|number for input flexibility and revert the payload spec:

-	exp?: number | string
+	exp?: number
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exp?: number | string
++ b/src/index.ts
@@ -86,7 +86,7 @@
// ... other fields in JWTPayloadSpec ...
exp?: number
// ... remaining fields ...
🤖 Prompt for AI Agents
In src/index.ts around line 89, revert JWTPayloadSpec.exp to type number only
(remove the union with string) so the interface matches the runtime schema at
~line 221 (t.Optional(t.Number())) and RFC 7519; keep JWTPayloadInput.exp as
string|number for input parsing elsewhere if present. Update the type
declaration at line 89 from `exp?: number | string` to `exp?: number`, and
ensure any downstream usages or validators expect a NumericDate (number) rather
than a string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants