Files
supabase/apps/studio/components/interfaces/Functions/httpHeaderAddActions.test.ts
Vaibhav 1cffe632e3 fix: webhook apikey (#47317)
## TL;DR
Database webhooks/Cron jobs now add `apikey: <secret-key>` for edge
function auth..

## ref:
- related to: https://github.com/supabase/supabase/pull/46890
- towards COM-269

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## New Features
* Improved edge function webhook authentication by automatically
selecting the appropriate API key or authorization header format.
* Authorization headers are now added or normalized when required, while
preserving existing custom headers and supported credentials.

## Improvements
* Simplified “Add header” and “Add parameter” controls with clearer
labels.
* Updated authentication actions to clearly describe the selected header
type.

## Tests
* Expanded coverage for key formats, authorization behavior, header
preservation, and revised control labels.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Tomás Pozo <tomaspozo@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-24 10:06:36 -06:00

160 lines
4.8 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
buildEdgeFunctionHeaderAddActions,
ensureEdgeFunctionAuthorizationHeader,
getEdgeFunctionAuthHeader,
} from './httpHeaderAddActions'
describe('buildEdgeFunctionHeaderAddActions', () => {
it('includes the apikey header for secret keys', () => {
const [authAction] = buildEdgeFunctionHeaderAddActions({
apiKey: 'sb_secret_123',
createRow: (name, value) => ({ name, value }),
})
expect(authAction.label).toBe('Add secret key')
expect(authAction.description).toBe(
'Requires JWT verification to be disabled and authorization handled by the function'
)
expect(authAction.createRows()).toEqual([{ name: 'apikey', value: 'sb_secret_123' }])
})
it('falls back to the Authorization header for legacy keys', () => {
const [authAction] = buildEdgeFunctionHeaderAddActions({
apiKey: 'legacy-service-role-jwt',
createRow: (name, value) => ({ name, value }),
})
expect(authAction.label).toBe('Add secret key')
expect(authAction.description).toBe('Required for edge functions that enforce JWT verification')
expect(authAction.createRows()).toEqual([
{ name: 'Authorization', value: 'Bearer legacy-service-role-jwt' },
])
})
})
describe('getEdgeFunctionAuthHeader', () => {
it('returns the apikey header for publishable keys', () => {
expect(getEdgeFunctionAuthHeader('sb_publishable_123')).toEqual({
name: 'apikey',
value: 'sb_publishable_123',
})
})
it('returns the Authorization header for non-prefixed keys', () => {
expect(getEdgeFunctionAuthHeader('legacy-service-role-jwt')).toEqual({
name: 'Authorization',
value: 'Bearer legacy-service-role-jwt',
})
})
it.each(['sb_secretly_123', 'sb_publishableish_123'])(
'does not treat %s as a new API key',
(apiKey) => {
expect(getEdgeFunctionAuthHeader(apiKey)).toEqual({
name: 'Authorization',
value: `Bearer ${apiKey}`,
})
}
)
})
describe('ensureEdgeFunctionAuthorizationHeader', () => {
const createRow = (name: string, value: string) => ({ id: 'new', name, value })
it('preserves apikey while normalizing Authorization rows', () => {
const headers = [
{ id: 'custom-before', name: 'X-Before', value: 'before' },
{ id: 'authorization', name: ' authorization ', value: 'Bearer old-key' },
{ id: 'apikey', name: 'apikey', value: 'sb_secret_123' },
{ id: 'duplicate', name: 'AUTHORIZATION', value: 'Bearer stale-key' },
{ id: 'custom-after', name: 'X-After', value: 'after' },
]
expect(
ensureEdgeFunctionAuthorizationHeader({
headers,
serviceRoleKey: 'legacy-service-role-jwt',
verifyJwt: true,
createRow,
})
).toEqual([
{ id: 'custom-before', name: 'X-Before', value: 'before' },
{
id: 'authorization',
name: 'Authorization',
value: 'Bearer old-key',
},
{ id: 'apikey', name: 'apikey', value: 'sb_secret_123' },
{ id: 'custom-after', name: 'X-After', value: 'after' },
])
})
it('adds Authorization alongside apikey when JWT verification is enabled', () => {
const headers = [{ id: 'apikey', name: 'apikey', value: 'sb_secret_123' }]
expect(
ensureEdgeFunctionAuthorizationHeader({
headers,
serviceRoleKey: 'legacy-service-role-jwt',
verifyJwt: true,
createRow,
})
).toEqual([
{ id: 'apikey', name: 'apikey', value: 'sb_secret_123' },
{ id: 'new', name: 'Authorization', value: 'Bearer legacy-service-role-jwt' },
])
})
it('does not change headers without an applicable service role key', () => {
const headers = [
{ id: 'authorization', name: 'Authorization', value: 'Bearer existing-key' },
{ id: 'apikey', name: 'apikey', value: 'sb_secret_123' },
]
expect(
ensureEdgeFunctionAuthorizationHeader({
headers,
serviceRoleKey: 'legacy-service-role-jwt',
verifyJwt: false,
createRow,
})
).toBe(headers)
expect(ensureEdgeFunctionAuthorizationHeader({ headers, verifyJwt: true, createRow })).toBe(
headers
)
expect(
ensureEdgeFunctionAuthorizationHeader({
headers,
serviceRoleKey: 'sb_secret_456',
verifyJwt: true,
createRow,
})
).toBe(headers)
})
it.each(['Bearer user-entered-token', ''])(
'preserves a user-entered Authorization value: %j',
(value) => {
const headers = [
{
id: 'authorization',
name: 'Authorization',
value,
},
{ id: 'apikey', name: 'apikey', value: 'sb_secret_123' },
]
expect(
ensureEdgeFunctionAuthorizationHeader({
headers,
serviceRoleKey: 'legacy-service-role-jwt',
verifyJwt: true,
createRow,
})
).toBe(headers)
}
)
})