-
Notifications
You must be signed in to change notification settings - Fork 450
Expand file tree
/
Copy pathtokenFreshness.ts
More file actions
52 lines (47 loc) · 1.91 KB
/
tokenFreshness.ts
File metadata and controls
52 lines (47 loc) · 1.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import type { JWT, TokenResource } from '@clerk/shared/types';
function asJwt(input: TokenResource | JWT): JWT | undefined {
return 'getRawString' in input ? input.jwt : input;
}
/**
* Picks the freshest of two tokens. Returns whichever argument has the more
* recent claim freshness. On a full tie (same oiat AND same iat) it returns
* `incoming`, since two tokens with identical timestamps can still differ in
* other claims (azp, org_id, etc.) during a token-format rollout, so the
* guard should only suppress when existing is strictly fresher.
*
* All origin-minted tokens carry the `oiat` JWT header (origin-issued-at;
* timestamp when claims were last assembled from the DB). A token without
* `oiat` is from a pre-feature codebase and is by definition staler than any
* token that has one.
*
* Used by the cross-tab broadcast handler in tokenCache to drop stale
* edge-minted tokens that would otherwise clobber a fresher cached entry.
*
* @internal
*/
export function pickFreshestJwt<T extends TokenResource | JWT>(existing: T, incoming: T): T {
const existingOiat = asJwt(existing)?.header?.oiat;
const incomingOiat = asJwt(incoming)?.header?.oiat;
if (existingOiat == null && incomingOiat == null) {
return incoming;
}
if (incomingOiat == null) {
return existing;
}
if (existingOiat == null) {
return incoming;
}
if (existingOiat > incomingOiat) {
return existing;
}
if (existingOiat < incomingOiat) {
return incoming;
}
// Equal oiat: tie-break by iat (more recent mint wins). On a full tie,
// return incoming: two tokens with identical oiat+iat may still differ
// in other claims (azp, org_id, etc.) added in a token-format rollout,
// so we only suppress when existing is strictly fresher.
const existingIat = asJwt(existing)?.claims?.iat ?? 0;
const incomingIat = asJwt(incoming)?.claims?.iat ?? 0;
return existingIat > incomingIat ? existing : incoming;
}