Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/core/verify-credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions src/core/verify-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading