From c225b1397df5265ae4a330895c9967282c7c3e0e Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Wed, 19 Aug 2026 14:14:15 +0200 Subject: [PATCH] feat(functions)!: rename set_auth_token to auth_token The capability was specified as a mutator: "Update the authorization token sent with all subsequent function invocations." That prescribes a shape rather than a behaviour, and the shape it prescribes is the one two SDKs have now removed. supabase-swift replaced FunctionsClient.setAuth(token:) with an access token closure resolved per request, and supabase-flutter is removing FunctionsClient.setAccessToken() for the same reason: a pinned token outlives the session that was current when it was set, shadowing that session across refreshes and sign-outs with nothing to clear it. supabase-csharp never had a setter at all and sits at partially_implemented purely because of the wording. Renamed to functions.invocation.auth_token, described in terms of what a caller can observe: which token an invocation sends, and which source wins. A stateful setter still satisfies it; it is no longer the only thing that does. Realtime's set_auth_token keeps its name. It genuinely is a mutator, since it holds a live socket and pushes a new token over it rather than attaching one per request, and both PRs above deliberately kept it. cross_client_token_sync had the same problem in its description, which specified propagation on auth state change events as the mechanism. supabase-py and supabase-csharp already carry notes explaining that they do it another way, and per-request resolution now makes a third. Reworded around the guarantee, leaving the mechanism open. Refs: supabase/supabase-swift#1233, supabase/supabase-flutter#1739 --- capabilities/client.yaml | 2 +- capabilities/functions.yaml | 6 ++--- specs/functions/invocation/auth_token.md | 28 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 specs/functions/invocation/auth_token.md diff --git a/capabilities/client.yaml b/capabilities/client.yaml index 22f125f..f60a6fe 100644 --- a/capabilities/client.yaml +++ b/capabilities/client.yaml @@ -18,7 +18,7 @@ features: group: authentication_integration - id: client.authentication_integration.cross_client_token_sync name: Cross-Client Token Synchronization - description: The client listens for auth state change events and automatically propagates the current JWT to all sub-clients (database, realtime, storage, functions) so requests stay authenticated without manual intervention. + description: Every sub-client (database, realtime, storage, functions) stays authenticated with the current JWT as the session signs in, refreshes, and signs out, without the caller updating any of them by hand. Whether the client pushes the token on auth state change events or each sub-client resolves it per request is an implementation detail. group: authentication_integration - id: client.authentication_integration.oauth_flow_type name: OAuth Flow Type Selection diff --git a/capabilities/functions.yaml b/capabilities/functions.yaml index ed16728..9989a73 100644 --- a/capabilities/functions.yaml +++ b/capabilities/functions.yaml @@ -12,9 +12,9 @@ features: name: Invoke Function description: Call a deployed Edge Function by name with an optional request body. group: invocation - - id: functions.invocation.set_auth_token - name: Set Auth Token - description: Update the authorization token sent with all subsequent function invocations. + - id: functions.invocation.auth_token + name: Invocation Auth Token + description: Supply the authorization token sent with function invocations, fixed at client construction, resolved per request from an access token provider, or overridden on a single call. group: request_configuration - id: functions.invocation.method_override name: HTTP Method Override diff --git a/specs/functions/invocation/auth_token.md b/specs/functions/invocation/auth_token.md new file mode 100644 index 0000000..b2d9839 --- /dev/null +++ b/specs/functions/invocation/auth_token.md @@ -0,0 +1,28 @@ +# Invocation Auth Token + +Determine which authorization token a function invocation sends, without requiring the SDK to hold a mutable token on the functions client. + +## Behavior + +An invocation sends an `Authorization` header resolved from the first of these that applies: + +1. **Per-call override.** A header passed to a single invocation wins over everything else, and applies only to that call. +2. **Access token provider.** A token supplied by the client, resolved fresh on every request. For a functions client owned by a `SupabaseClient`, this is the current session token, so the header follows sign-in, refresh, and sign-out with no further action. For a standalone functions client, this is whatever provider the caller passed at construction. +3. **Construction-time header.** An `Authorization` header passed when the functions client is built, used when there is no provider. + +If none apply, the invocation carries no `Authorization` header. The API key header, where the SDK sends one, is independent of this capability. + +An SDK satisfies this capability through any combination of the three that covers per-call override plus one non-per-call source. A stateful `setAuthToken(token)` mutator is one such source, but it is not required, and is not the preferred shape where the SDK can resolve a token per request instead. + +## Notes + +- **A per-call override must beat a live session token.** An SDK whose auth layer unconditionally overwrites `Authorization` breaks point 1 even though it appears to implement the capability. This is worth an explicit test rather than an assumption. +- **A stateful setter is a shadowing hazard.** A token pinned by a mutator outlives the session that was current when it was set: it keeps being sent after that session refreshes or signs out, and nothing clears it. SDKs that resolve the token per request avoid this by construction. +- Where a mutator does exist, its scope is the functions client alone. Cross-client propagation is [Cross-Client Token Synchronization](client.authentication_integration.cross_client_token_sync). + +## Related + +- [Invoke Function](functions.invocation.invoke) — the call this configures +- [Set Auth Token](realtime.client.set_auth_token) — Realtime's equivalent, which *is* a mutator: it holds a live socket and pushes a new token over it rather than attaching one per request +- [Cross-Client Token Synchronization](client.authentication_integration.cross_client_token_sync) — how the umbrella client keeps this token current +- [Third-Party Auth](client.authentication_integration.third_party_auth) — the construction-time token callback that feeds the provider in point 2