Skip to content

Commit 3b558c3

Browse files
committed
fix(clerk-js): pickFreshestJwt returns incoming on full tie
Two tokens with identical oiat+iat may still differ in other claims (azp, org_id, etc.) added in a token-format rollout. The previous 'no churn on tie' rule suppressed legitimate updates and caused the backend to read stale claim sets, redirecting to /sign-in. Only suppress when existing is strictly fresher.
1 parent 78b3328 commit 3b558c3

3 files changed

Lines changed: 46 additions & 3 deletions

File tree

.husky/_/husky.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env sh
2+
if [ -z "$husky_skip_init" ]; then
3+
debug () {
4+
if [ "$HUSKY_DEBUG" = "1" ]; then
5+
echo "husky (debug) - $1"
6+
fi
7+
}
8+
9+
readonly hook_name="$(basename -- "$0")"
10+
debug "starting $hook_name..."
11+
12+
if [ "$HUSKY" = "0" ]; then
13+
debug "HUSKY env variable is set to 0, skipping hook"
14+
exit 0
15+
fi
16+
17+
if [ -f ~/.huskyrc ]; then
18+
debug "sourcing ~/.huskyrc"
19+
. ~/.huskyrc
20+
fi
21+
22+
readonly husky_skip_init=1
23+
export husky_skip_init
24+
sh -e "$0" "$@"
25+
exitCode="$?"
26+
27+
if [ $exitCode != 0 ]; then
28+
echo "husky - $hook_name hook exited with code $exitCode (error)"
29+
fi
30+
31+
if [ $exitCode = 127 ]; then
32+
echo "husky - command not found in PATH=$PATH"
33+
fi
34+
35+
exit $exitCode
36+
fi

packages/clerk-js/src/core/__tests__/tokenFreshness.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,13 @@ describe('pickFreshestJwt', () => {
4646
expect(pickFreshestJwt(existing, incoming)).toBe(incoming);
4747
});
4848

49-
it('picks existing when oiat equal and iat equal (identical, no churn)', () => {
49+
it('picks incoming when oiat equal and iat equal (other claims may differ)', () => {
50+
// Two tokens with identical oiat+iat may still differ in other claims
51+
// (azp, org_id, etc.) during a token-format rollout. Only suppress when
52+
// existing is strictly fresher; on full ties, let incoming through.
5053
const existing = makeToken({ oiat: 100, iat: 150 });
5154
const incoming = makeToken({ oiat: 100, iat: 150 });
52-
expect(pickFreshestJwt(existing, incoming)).toBe(existing);
55+
expect(pickFreshestJwt(existing, incoming)).toBe(incoming);
5356
});
5457

5558
it('picks existing when oiat equal and incoming iat missing (treated as 0)', () => {

packages/clerk-js/src/core/tokenFreshness.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ export function pickFreshestJwt<T extends TokenResource | JWT>(existing: T, inco
4242
return incoming;
4343
}
4444

45+
// Equal oiat: tie-break by iat (more recent mint wins). On a full tie,
46+
// return incoming: two tokens with identical oiat+iat may still differ
47+
// in other claims (azp, org_id, etc.) added in a token-format rollout,
48+
// so we only suppress when existing is strictly fresher.
4549
const existingIat = asJwt(existing)?.claims?.iat ?? 0;
4650
const incomingIat = asJwt(incoming)?.claims?.iat ?? 0;
47-
return existingIat >= incomingIat ? existing : incoming;
51+
return existingIat > incomingIat ? existing : incoming;
4852
}

0 commit comments

Comments
 (0)