Skip to content

fix(functions): stop sending API key in Authorization header for function calls#2511

Merged
mandarini merged 2 commits into
masterfrom
fix/functions-omit-apikey-authorization
Jul 13, 2026
Merged

fix(functions): stop sending API key in Authorization header for function calls#2511
mandarini merged 2 commits into
masterfrom
fix/functions-omit-apikey-authorization

Conversation

@mandarini

@mandarini mandarini commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Edge Function calls (supabase.functions.invoke()) no longer put the project API key in the Authorization header when there is no user session. The key is sent only in the apikey header, and Authorization is reserved for a real user (or custom) session token, matching the Server SDK pattern.

What changed?

  • packages/core/supabase-js/src/lib/fetch.ts: fetchWithAuth accepts a new optional { isFunctionsClient } flag. When set, it no longer falls back to sending the API key as Authorization: Bearer <key>, but only for new-format keys (sb_publishable_... / sb_secret_...). A real session/custom token is still sent, and the apikey header is unchanged.
  • packages/core/supabase-js/src/SupabaseClient.ts: added a dedicated functionsFetch built with { isFunctionsClient: true }. The functions getter uses it; REST (PostgREST), Storage, and Realtime keep the shared fetch and are unaffected.
  • packages/core/functions-js/src/FunctionsClient.ts: updated the invoke JSDoc to describe the apikey vs Authorization header semantics.
  • docs/MIGRATION.md: added an "Edge Functions auth headers" section documenting the behavior and the one edge case that is affected.
  • Tests: added unit coverage in fetch.test.ts for the isFunctionsClient option and an integration case in SupabaseClient.test.ts proving functions omit Authorization for a new-format key with no session while other services do not.

Why was this change needed?

New-format API keys (sb_publishable_... / sb_secret_...) are not JWTs, so sending them as Authorization: Bearer <key> caused the gateway to reject the request with "invalid token format." A platform apikey-compatibility patch (now GA) lets verify_jwt=true functions be called with only the apikey header, so the SDK no longer needs to duplicate the key into Authorization. This aligns Functions with the Server SDK convention: Authorization is for user/custom tokens only.

Closes SDK-1050.

Screenshots/Examples

Unauthenticated function call with a new-format key:

Before:

apikey: sb_publishable_...
Authorization: Bearer sb_publishable_...   # rejected as "invalid token format"

After:

apikey: sb_publishable_...
# no Authorization header

Authenticated call (unchanged):

apikey: sb_publishable_...
Authorization: Bearer <user_jwt>

Breaking changes

The change is scoped to Edge Functions only, and legacy JWT keys keep their existing behavior (still sent in Authorization for backward compatibility), so this is not a breaking change in practice. The only impacted case is code that reads the API key out of the Authorization header inside an Edge Function; those should read the apikey header instead, or migrate to @supabase/server.

  • This PR contains no breaking changes

Checklist

  • I have read the Contributing Guidelines
  • My PR title follows the conventional commit format: <type>(<scope>): <description>
  • I have run pnpm nx format to ensure consistent code formatting
  • I have added tests for new functionality (if applicable)
  • I have updated documentation (if applicable)

Additional notes

Scope is intentionally limited to new-format keys so there is zero regression risk for self-hosted or legacy-key users on older gateways. Moving to an unconditional drop for all key types later is a one-line change (removing the key-format guard in fetch.ts), best done on v3 or once apikey-compatibility is universal.

@mandarini mandarini self-assigned this Jul 10, 2026
@github-actions github-actions Bot added the functions-js Related to the functions-js library. label Jul 10, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@supabase/auth-js

npm i https://pkg.pr.new/@supabase/auth-js@2511

@supabase/functions-js

npm i https://pkg.pr.new/@supabase/functions-js@2511

@supabase/postgrest-js

npm i https://pkg.pr.new/@supabase/postgrest-js@2511

@supabase/realtime-js

npm i https://pkg.pr.new/@supabase/realtime-js@2511

@supabase/storage-js

npm i https://pkg.pr.new/@supabase/storage-js@2511

@supabase/supabase-js

npm i https://pkg.pr.new/@supabase/supabase-js@2511

commit: 7500549

@coveralls

coveralls commented Jul 10, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 80.484% (+0.04%) from 80.446% — fix/functions-omit-apikey-authorization into master

kallebysantos
kallebysantos previously approved these changes Jul 10, 2026
@mandarini
mandarini marked this pull request as ready for review July 10, 2026 12:29
@mandarini
mandarini requested review from a team as code owners July 10, 2026 12:29
Tr00d
Tr00d previously approved these changes Jul 10, 2026
grdsdev
grdsdev previously approved these changes Jul 10, 2026
Comment thread packages/core/supabase-js/src/lib/fetch.ts Outdated
Comment thread packages/core/supabase-js/src/lib/fetch.ts Outdated
Comment thread packages/core/supabase-js/src/lib/fetch.ts Outdated
Comment thread packages/core/supabase-js/src/lib/fetch.ts
Comment thread packages/core/supabase-js/src/SupabaseClient.ts Outdated
@mandarini mandarini added the do-not-merge Do not merge this PR. label Jul 10, 2026
…ader logic

Future-proof the sb_ key family per auth-team guidance (stojan): createClient()
now validates the key format at construction and throws for a key that starts with
sb_ but is not a recognized subtype (sb_publishable_ / sb_secret_), signalling that
the SDK must be upgraded to support the new type. Legacy JWT keys (no sb_ prefix)
and the two recognized subtypes are unaffected, so no key that works today starts
failing — this is not a breaking change.

Also addresses review feedback on fetch.ts:
- rename the fetchWithAuth option isFunctionsClient -> omitApiKeyAsBearer so the
  generic util no longer knows about a "functions client"
- compute the key-as-Bearer decision once at construction instead of per request
- drop the accessToken === supabaseKey equality proxy; split a raw _getSessionToken()
  (null when no session) from the coalescing _getAccessToken(), and feed the raw
  getter to the fetch wrappers so Authorization fallback lives in one place
- collapse the triplicated rationale comment to a single canonical location

Realtime keeps using _getAccessToken(); PostgREST/Storage behavior is unchanged.
@mandarini
mandarini dismissed stale reviews from grdsdev, Tr00d, and kallebysantos via 7500549 July 13, 2026 13:19
@mandarini mandarini removed the do-not-merge Do not merge this PR. label Jul 13, 2026

@kallebysantos kallebysantos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 💚

mandarini pushed a commit to supabase/ssr that referenced this pull request Jul 14, 2026
This PR updates `@supabase/supabase-js` to v2.110.5.

**Source**: supabase-js-stable-release

---

## Release Notes

## v2.110.5

## 2.110.5 (2026-07-14)

### 🩹 Fixes

- **supabase:** avoid edge runtime warning
([#2522](supabase/supabase-js#2522))

### ❤️ Thank You

- Vaibhav @7ttp
## v2.110.4

## 2.110.4 (2026-07-14)

### 🩹 Fixes

- **functions:** stop sending API key in Authorization header for
function calls
([#2511](supabase/supabase-js#2511))
- **realtime:** encode broadcast header fields as UTF-8
([#2516](supabase/supabase-js#2516))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
- Pedro Henrique

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
mandarini pushed a commit to supabase/supabase that referenced this pull request Jul 14, 2026
This PR updates @supabase/*-js libraries to version 2.110.5.

**Source**: supabase-js-stable-release

**Changes**:
- Updated @supabase/supabase-js to 2.110.5
- Updated @supabase/auth-js to 2.110.5
- Updated @supabase/realtime-js to 2.110.5
- Updated @supabase/postgest-js to 2.110.5
- Refreshed pnpm-lock.yaml

---

## Release Notes

## v2.110.5

## 2.110.5 (2026-07-14)

### 🩹 Fixes

- **supabase:** avoid edge runtime warning
([#2522](supabase/supabase-js#2522))

### ❤️ Thank You

- Vaibhav @7ttp
## v2.110.4

## 2.110.4 (2026-07-14)

### 🩹 Fixes

- **functions:** stop sending API key in Authorization header for
function calls
([#2511](supabase/supabase-js#2511))
- **realtime:** encode broadcast header fields as UTF-8
([#2516](supabase/supabase-js#2516))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
- Pedro Henrique
## v2.110.3

## 2.110.3 (2026-07-13)

### 🩹 Fixes

- **auth:** preserve pkce verifier
([#2513](supabase/supabase-js#2513))
- **postgrest:** pin tstyche target off floating latest
([#2509](supabase/supabase-js#2509))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
- Vaibhav @7ttp
## v2.110.2

## 2.110.2 (2026-07-09)

### 🩹 Fixes

- **auth:** clear local session on signout failures
([#2504](supabase/supabase-js#2504))

### ❤️ Thank You

- Luc Peng

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
mandarini pushed a commit to supabase/supabase that referenced this pull request Jul 15, 2026
This PR updates @supabase/*-js libraries to version 2.110.6.

**Source**: supabase-js-stable-release

**Changes**:
- Updated @supabase/supabase-js to 2.110.6
- Updated @supabase/auth-js to 2.110.6
- Updated @supabase/realtime-js to 2.110.6
- Updated @supabase/postgest-js to 2.110.6
- Refreshed pnpm-lock.yaml

---

## Release Notes

## v2.110.6

## 2.110.6 (2026-07-15)

### 🩹 Fixes

- **postgrest:** type hinted self-referencing embeds as arrays
([#2520](supabase/supabase-js#2520))
- **realtime:** forward opts to send() in track()
([#2490](supabase/supabase-js#2490))
- **supabase:** warn instead of throw for unrecognized sb_ API key
subtypes ([#2526](supabase/supabase-js#2526))

### ❤️ Thank You

- Franco Kaddour @FrancoKaddour
- Katerina Skroumpelou @mandarini
## v2.110.5

## 2.110.5 (2026-07-14)

### 🩹 Fixes

- **supabase:** avoid edge runtime warning
([#2522](supabase/supabase-js#2522))

### ❤️ Thank You

- Vaibhav @7ttp
## v2.110.4

## 2.110.4 (2026-07-14)

### 🩹 Fixes

- **functions:** stop sending API key in Authorization header for
function calls
([#2511](supabase/supabase-js#2511))
- **realtime:** encode broadcast header fields as UTF-8
([#2516](supabase/supabase-js#2516))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
- Pedro Henrique
## v2.110.3

## 2.110.3 (2026-07-13)

### 🩹 Fixes

- **auth:** preserve pkce verifier
([#2513](supabase/supabase-js#2513))
- **postgrest:** pin tstyche target off floating latest
([#2509](supabase/supabase-js#2509))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
- Vaibhav @7ttp
## v2.110.2

## 2.110.2 (2026-07-09)

### 🩹 Fixes

- **auth:** clear local session on signout failures
([#2504](supabase/supabase-js#2504))

### ❤️ Thank You

- Luc Peng

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions-js Related to the functions-js library.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants