diff --git a/browse/src/token-registry.ts b/browse/src/token-registry.ts index 89b0a990c..b241e55e4 100644 --- a/browse/src/token-registry.ts +++ b/browse/src/token-registry.ts @@ -377,7 +377,9 @@ export function exchangeSetupKey(setupKey: string, sessionExpiresSeconds?: numbe /** * Validate a token and return its info if valid. - * Returns null for expired, revoked, or unknown tokens. + * Returns null for expired, revoked, or unknown tokens, and for unexchanged + * setup keys: a setup key authenticates only the /connect exchange, never a + * bearer request. * Root token returns a special root info object. */ export function validateToken(token: string): TokenInfo | null { @@ -396,7 +398,7 @@ export function validateToken(token: string): TokenInfo | null { } const info = tokens.get(token); - if (!info) return null; + if (!info || info.type !== 'session') return null; // Check expiry if (info.expiresAt && new Date(info.expiresAt) < new Date()) { diff --git a/browse/test/token-registry.test.ts b/browse/test/token-registry.test.ts index 3d2639b83..77b665f74 100644 --- a/browse/test/token-registry.test.ts +++ b/browse/test/token-registry.test.ts @@ -233,15 +233,19 @@ describe('token-registry', () => { it('rejects expired setup key', () => { const setup = createSetupKey({}); - // Manually expire it - const info = validateToken(setup.token); - if (info) { - (info as any).expiresAt = new Date(Date.now() - 1000).toISOString(); - } + // Manually expire it (createSetupKey returns the registry's own record) + setup.expiresAt = new Date(Date.now() - 1000).toISOString(); const session = exchangeSetupKey(setup.token); expect(session).toBeNull(); }); + it('does not accept an unexchanged setup key as a bearer token', () => { + const setup = createSetupKey({ scopes: ['read', 'write'] }); + expect(validateToken(setup.token)).toBeNull(); + const session = exchangeSetupKey(setup.token); + expect(validateToken(session!.token)).not.toBeNull(); + }); + it('rejects unknown setup key', () => { expect(exchangeSetupKey('gsk_setup_nonexistent')).toBeNull(); });