diff --git a/src/core/verify-credentials.test.ts b/src/core/verify-credentials.test.ts index ef0bffb..5f950ab 100644 --- a/src/core/verify-credentials.test.ts +++ b/src/core/verify-credentials.test.ts @@ -655,6 +655,36 @@ describe('verifyCredentials', () => { expect(result.error!.code).toBe(InvalidCredentialsError) }) + it('falls through to secret when Authorization carries an sb_ secret', async () => { + // supabase-js forwards `sb_*` keys in BOTH headers; the JWT check must + // skip rather than reject so the `secret` mode can match the apikey. + const creds: Credentials = { + token: 'sb_secret_xyz', + apikey: 'sb_secret_xyz', + } + const result = await verifyCredentials(creds, { + auth: ['user', 'secret'], + env: makeEnv({ jwks }), + }) + expect(result.error).toBeNull() + expect(result.data!.authMode).toBe('secret') + expect(result.data!.keyName).toBe('default') + }) + + it('falls through to publishable when Authorization carries an sb_ publishable key', async () => { + const creds: Credentials = { + token: 'sb_publishable_xyz', + apikey: 'sb_publishable_xyz', + } + const result = await verifyCredentials(creds, { + auth: ['user', 'publishable'], + env: makeEnv({ jwks }), + }) + expect(result.error).toBeNull() + expect(result.data!.authMode).toBe('publishable') + expect(result.data!.keyName).toBe('default') + }) + it('rejects JWT with missing sub claim', async () => { const { privateKey, publicKey } = await generateKeyPair('RS256') const publicJwk = await exportJWK(publicKey) diff --git a/src/core/verify-credentials.ts b/src/core/verify-credentials.ts index 5c2d1e6..13344cb 100644 --- a/src/core/verify-credentials.ts +++ b/src/core/verify-credentials.ts @@ -208,6 +208,11 @@ async function tryMode( case 'user': { if (!credentials.token) return null + // The Supabase SDK forwards `sb_*` secrets in the Authorization header + // alongside the apikey header. Treat them as not-applicable here so the + // chain falls through to `secret` / `publishable` instead of failing + // JWT verification. + if (credentials.token.startsWith('sb_')) return null if (!env.jwks) return null try { const jwkSet = getJwksResolver(env.jwks)