You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
IdPOAuthAccessToken.fromJwtPayload set expiration, createdAt, and updatedAt from the JWT's raw second-based exp/iat, but M2MToken and the API response expose those fields in milliseconds. So the same OAuth token's timestamps came out 1000x off depending on whether you verified it as a JWT or fetched it from the API, and token.expiration < Date.now() was always true.
The fix is the * 1000 conversion that was already being done everywhere else:
payload.exp*1000// was: payload.exp
Token validity itself was never affected since the expired boolean already used * 1000, only the exposed timestamps were wrong. The OAuth-JWT verify test didn't assert these fields, which is why it slipped through, so I added that coverage.
Summary by CodeRabbit
Bug Fixes
OAuth access token verification now returns timestamp fields (expiration, createdAt, updatedAt) in milliseconds instead of seconds, ensuring consistency with other token types and API responses.
This PR fixes timestamp field values in IdPOAuthAccessToken when verifying OAuth access tokens as JWTs. The expiration, createdAt, and updatedAt fields are now populated in milliseconds by converting JWT exp/iat claim values (which are in seconds) via multiplication by 1000, aligning with M2MToken behavior and API responses.
Changes
OAuth Token Millisecond Timestamps
Layer / File(s)
Summary
Millisecond timestamp conversion in OAuth token factory packages/backend/src/api/resources/IdPOAuthAccessToken.ts
IdPOAuthAccessToken.fromJwtPayload multiplies JWT exp and iat claim values by 1000 when setting expiration, createdAt, and updatedAt.
Test assertions and release documentation packages/backend/src/tokens/__tests__/verify.test.ts, .changeset/idp-oauth-token-ms-timestamps.md
OAuth JWT verification test assertions verify millisecond timestamps, and a patch changeset documents the timestamp field consistency fix.
🎯 2 (Simple) | ⏱️ ~8 minutes
A rabbit hops through timestamps with glee,
Seconds now dance to millisecond decree,
OAuth tokens sing in sync, unified time,
API and backend aligned in their rhyme. 🐰⏰
The title directly and concisely describes the main fix: converting IdPOAuthAccessToken JWT timestamps from seconds to milliseconds, which is the core purpose of this changeset.
Docstring Coverage
✅ Passed
No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check
✅ Passed
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
✅ Passed
Check skipped because no linked issues were found for this pull request.
Description Check
✅ Passed
Check skipped - CodeRabbit’s high-level summary is enabled.
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches📝 Generate docstrings
Create stacked PR
Commit on current branch
Comment @coderabbitai help to get the list of available commands and usage tips.
Generated by Break Check on 2026-06-08T00:02:43.956Z
Summary
Metric
Count
Packages analyzed
19
Packages with changes
1
🔴 Breaking changes
0
🟡 Non-breaking changes
1
🟢 Additions
0
🤖 This report was reviewed by claude-sonnet-4-6.
Note
Break Check could not snapshot 3 subpaths; the diff below excludes them.
@clerk/astro ./env: Internal Error: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
@clerk/shared ./cookie: Internal Error: Unable to follow symbol for "Cookies" You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
@clerk/testing ./cypress: Symbol not found for identifier: Cypress
@clerk/shared
Current version: 4.15.0 Recommended bump: MINOR → 4.16.0
Static analyzer: Breaking change in function apiUrlFromPublishableKey: Return type changed: "https://api.clerk.com"|"https://api.lclclerk.com"|"https://api.clerkstage.dev" → "https://api.lclclerk.com"|"https://api.clerkstage.dev"|"https://api.clerk.com"
🤖 AI review (reclassified as non-breaking) (98%): The return type is a union of the exact same three string literals; only the order of union members changed, which is structurally identical in TypeScript — no well-typed consumer code is affected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
IdPOAuthAccessToken.fromJwtPayloadsetexpiration,createdAt, andupdatedAtfrom the JWT's raw second-basedexp/iat, butM2MTokenand the API response expose those fields in milliseconds. So the same OAuth token's timestamps came out 1000x off depending on whether you verified it as a JWT or fetched it from the API, andtoken.expiration < Date.now()was always true.The fix is the
* 1000conversion that was already being done everywhere else:Token validity itself was never affected since the
expiredboolean already used* 1000, only the exposed timestamps were wrong. The OAuth-JWT verify test didn't assert these fields, which is why it slipped through, so I added that coverage.Summary by CodeRabbit