Repository navigation
fix(clerk-js): Account for user clock skew in TokenCache #672
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f77c0d6
92eb14b
5bbf883
c3e94a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,5 @@ | ||
| import type { TokenResource } from '@clerk/types'; | ||
|
|
||
| import { clerkCoreErrorExpiredToken } from './errors'; | ||
|
|
||
| interface TokenCacheKeyJSON { | ||
| audience?: string; | ||
| tokenId: string; | ||
|
|
@@ -11,10 +9,12 @@ interface TokenCacheEntry extends TokenCacheKeyJSON { | |
| tokenResolver: Promise<TokenResource>; | ||
| } | ||
|
|
||
| type Seconds = number; | ||
|
|
||
| interface TokenCacheValue { | ||
| entry: TokenCacheEntry; | ||
| expiresAt?: number; | ||
| expiresIn?: number; | ||
| createdAt: Seconds; | ||
| expiresIn?: Seconds; | ||
| } | ||
|
|
||
| interface TokenCache { | ||
|
|
@@ -27,6 +27,8 @@ interface TokenCache { | |
| const KEY_PREFIX = 'clerk'; | ||
| const DELIMITER = '::'; | ||
| const LEEWAY = 10; | ||
| // This value should have the same value as the INTERVAL_IN_MS in AuthenticationPoller | ||
| const SYNC_LEEWAY = 5; | ||
|
|
||
| export class TokenCacheKey { | ||
| static fromKey(key: string): TokenCacheKey { | ||
|
|
@@ -63,7 +65,9 @@ const MemoryTokenCache = (prefix = KEY_PREFIX): TokenCache => { | |
| }); | ||
|
|
||
| const key = cacheKey.toKey(); | ||
| const value: TokenCacheValue = { entry }; | ||
|
|
||
| const createdAt = Math.floor(Date.now() / 1000); | ||
| const value: TokenCacheValue = { entry, createdAt }; | ||
|
|
||
| const deleteKey = () => { | ||
| if (cache.get(key) === value) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 馃檭 i'm not a big fun of this check since it compares 2
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey Dimitri, I might be wrong but I think this is meant to compare by reference, actually, but it's possible that we no longer need it. Keep in mind that before the setActive PR that we merged yesterday, the keys did not include the updateAt value. The The equality check in essence means that calling LMK if this makes sense :) However, now that we do have the updatedAt in the key, let's think about whether we need to keep the check or not. I think i'd keep it, as the key is produced outside of that class so no one enforces that all entries will have different keys. Ofc, we can always refactor this logic and remove the check. LMK your thoughts |
||
|
|
@@ -73,16 +77,11 @@ const MemoryTokenCache = (prefix = KEY_PREFIX): TokenCache => { | |
|
|
||
| entry.tokenResolver | ||
| .then(newToken => { | ||
| const nowSeconds = Math.floor(Date.now() / 1000); | ||
| const expiresAt = newToken.jwt.claims.exp; | ||
| const expiresIn = expiresAt - nowSeconds; | ||
|
|
||
| if (expiresIn <= 0) { | ||
| clerkCoreErrorExpiredToken(expiresAt); | ||
|
dimkl marked this conversation as resolved.
|
||
| } | ||
| const issuedAt = newToken.jwt.claims.iat; | ||
| const expiresIn: Seconds = expiresAt - issuedAt; | ||
|
|
||
| // Mutate cached value and set expirations | ||
| value.expiresAt = expiresAt; | ||
| value.expiresIn = expiresIn; | ||
| setTimeout(deleteKey, expiresIn * 1000); | ||
| }) | ||
|
|
@@ -102,9 +101,12 @@ const MemoryTokenCache = (prefix = KEY_PREFIX): TokenCache => { | |
| } | ||
|
|
||
| const nowSeconds = Math.floor(Date.now() / 1000); | ||
| const willExpireSoon = value.expiresAt && value.expiresAt - leeway < nowSeconds; | ||
|
nikosdouvlis marked this conversation as resolved.
|
||
| const elapsedSeconds = nowSeconds - value.createdAt; | ||
| // We will include the authentication poller interval as part of the leeway to ensure | ||
| // that the cache value will be valid for more than the SYNC_LEEWAY or the leeway in the next poll. | ||
| const expiresSoon = value.expiresIn! - elapsedSeconds < (leeway || 1) + SYNC_LEEWAY; | ||
|
|
||
| if (willExpireSoon) { | ||
| if (expiresSoon) { | ||
| cache.delete(cacheKey.toKey()); | ||
| return; | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.