mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
fix(auth): Convex auth readiness + JWT audience fallback for Clerk (#2638)
* fix(auth): wait for Convex auth readiness, handle missing JWT aud claim Root cause of "Authentication required" Convex errors and "Failed to load notification settings": ConvexClient sends mutations before WebSocket auth handshake completes, and validateBearerToken rejects standard Clerk session tokens that lack an `aud` claim. Fixes: - convex-client.ts: expose waitForConvexAuth() using setAuth's onChange callback, so callers can wait for the server to confirm auth - App.ts: claimSubscription now awaits waitForConvexAuth before mutation - checkout.ts: createCheckout awaits waitForConvexAuth before action - auth-session.ts: try jwtVerify with audience first (convex template), fall back without audience (standard Clerk session tokens have no aud) - DeckGLMap.ts: guard this.maplibreMap.style null before getLayer (Sentry WORLDMONITOR-JW, iOS Safari background tab kill) * fix(notifications): log error instead of silently swallowing it The catch block discarded the error entirely (no console, no Sentry). 401s and other failures were invisible. Now logs the actual error. * fix(auth): correct jose aud error check, guard checkout auth timeout P1: jose error message is '"aud" claim check failed', not 'audience'. The fallback for standard Clerk tokens was never triggering. P2: waitForConvexAuth result was ignored in startCheckout. Now falls back to pricing page on auth timeout instead of sending unauthenticated. * fix(auth): reset authReadyPromise on sign-out so re-auth waits properly * fix(auth): only fallback for missing aud, not wrong aud; reset auth on sign-out; guard checkout timeout - auth-session.ts: check 'missing required "aud"' not just '"aud"' so tokens with wrong audience are still rejected (fixes test) - convex-client.ts: reset authReadyPromise on sign-out (onChange false) - checkout.ts: bail on waitForConvexAuth timeout instead of proceeding * fix(sentry): filter Clerk removeChild DOM reconciliation noise (JV) Clerk SDK's internal Preact renderer throws 'The node to be removed is not a child of this node' with zero frames from our code. 9 events across 8 users, all inside clerk-*.js bundle. Cannot fix without SDK update. * fix(checkout): skip auth wait for signed-out users to avoid 10s stall waitForConvexAuth blocks until timeout for unauthenticated users since the promise can never resolve. Now only waits when getCurrentClerkUser() returns a signed-in user. Signed-out upgrade clicks fall through immediately to the pricing page. * test(auth): add test for missing-aud JWT fallback (standard Clerk tokens) Covers the case where a Clerk standard session token (no aud claim) is accepted by the audience fallback path, while tokens with a wrong aud are still rejected.
This commit is contained in:
@@ -215,6 +215,20 @@ describe('validateBearerToken (with JWKS)', () => {
|
||||
assert.equal(result.valid, false);
|
||||
});
|
||||
|
||||
it('accepts a standard Clerk token with no aud claim (fallback path)', async () => {
|
||||
const token = await new SignJWT({ sub: 'user_noaud', plan: 'pro' })
|
||||
.setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' })
|
||||
.setIssuer(`http://127.0.0.1:${jwksPort}`)
|
||||
.setSubject('user_noaud')
|
||||
.setIssuedAt()
|
||||
.setExpirationTime('1h')
|
||||
.sign(privateKey);
|
||||
|
||||
const result = await validateBearerToken(token);
|
||||
assert.equal(result.valid, true, 'standard Clerk tokens without aud should be accepted');
|
||||
assert.equal(result.userId, 'user_noaud');
|
||||
});
|
||||
|
||||
it('rejects a token with wrong issuer', async () => {
|
||||
const token = await new SignJWT({ sub: 'user_wrongiss', plan: 'pro' })
|
||||
.setProtectedHeader({ alg: 'RS256', kid: 'test-key-1' })
|
||||
|
||||
Reference in New Issue
Block a user