| name | lovable-cloud-edge-functions |
| description | Supabase edge function authentication, config.toml, and Lovable Cloud deployment. TRIGGER when: adding, modifying, or reviewing an edge function in a Lovable Cloud project, or modifying a file that configures one (e.g. supabase/config.toml) or invokes one (e.g. pg_cron). DO NOT TRIGGER when: no edge function configured, invoked, or modified.
|
| user-invocable | false |
Edge Function Authentication Framework
Background: see REFERENCES.md for the ES256/HS256 gateway constraint that shapes the tier table below.
Auth tiers
Tier 1: Browser-invoked functions
Functions called from the browser via supabase.functions.invoke().
| Setting | Value |
|---|
verify_jwt | false |
| In-code auth | User JWT validation (e.g., requireUserJwt() or equivalent) |
Why: The browser sends an ES256 user JWT. The gateway cannot verify it.
In-code auth is the only access control for these functions — there is no
defense-in-depth layer. This makes in-code auth removal especially dangerous
for browser-invoked functions.
These patterns were extracted from a production Lovable Cloud project and genericized. Adapt file paths and error-code namespaces for your project.
Your project will have its own auth utility location and naming convention for the user JWT validation helper.
In-code JWT validation must cryptographically verify the token signature (not just decode it) and check the exp claim. Decode-only is not sufficient — the gateway provides no backup for Tier 1.
Tier 2: Service-role / cron / trigger functions
Functions called by pg_cron, pg_net database triggers, or other edge functions
using the service role key. Not called by browsers.
| Setting | Value |
|---|
verify_jwt | true |
| In-code auth | Service-role key validation (e.g., requireServiceRole() or equivalent) |
Why: Service-role keys use HS256, which the gateway can verify. Both the
gateway and in-code auth enforce access — defense in depth.
Caution: verify_jwt = true accepts any HS256-signed JWT — including anon-role tokens issued to ordinary unauthenticated browsers. In-code auth must verify the role claim equals service_role in the decoded JWT payload (not a caller-supplied header value). Omitting this check allows anon callers to invoke service-role functions.
Tier 3: Webhook functions
Functions called by external services (Stripe, Lovable, etc.) that cannot
send a Supabase JWT of any kind.
| Setting | Value |
|---|
verify_jwt | false |
| In-code auth | Signature verification (Stripe signature, HMAC, etc.) |
Why: External callers have no Supabase JWT. The function authenticates the
caller by verifying a request signature specific to that service.
Critical: Signature verification must consume the raw request body (await req.arrayBuffer() or await req.text()) before any other body read. Parsing the body as JSON before signature verification corrupts the HMAC check — some implementations then silently fall through to processing the unverified payload.
Tier 4: Intentionally public functions
Functions that are designed to be publicly accessible (e.g., sitemap, health
check).
| Setting | Value |
|---|
verify_jwt | false |
| In-code auth | None — but must not expose secrets or sensitive data |
Why: No auth needed by design. The verify_jwt = false setting and
absence of in-code auth must be justified with a comment in config.toml
and in the function source.
config.toml rules
supabase/config.toml is security-critical infrastructure.
- Never regenerate, overwrite, or remove entries from
config.toml.
- Every edge function in
supabase/functions/ must have an explicit
[functions.<name>] section with a verify_jwt setting.
- Never bulk-flip existing
verify_jwt entries. Each function's setting
was chosen individually based on its tier. If a single function needs to
change, change that one function and document why.
- When changing a function's
verify_jwt setting, verify it matches the
correct tier above. Flipping a non-browser function from true to false
removes a defense layer and requires justification.
Adding a new edge function
- Determine the function's tier. If the caller is an external service (not a browser user, not your own cron/trigger, not an explicitly no-auth endpoint like a sitemap or health check) — it is Tier 3, not Tier 4. Both Tier 3 and Tier 4 use
verify_jwt = false, so miscategorizing Tier 3 as Tier 4 produces a silently open endpoint. Default to Tier 3 when uncertain about external callers.
- Add a
[functions.<name>] entry to config.toml with the correct
verify_jwt setting per the tier table above.
- Add a comment above the entry if
verify_jwt = false, stating which tier
applies and why.
- Add the corresponding in-code auth call for the tier.
- For browser-invoked functions: in-code auth is the only control.
Do not skip it.
In-code auth
Do not remove Authorization header validation or the in-code auth call from
edge functions.
For browser-invoked functions (Tier 1), in-code auth is the sole access
control — removing it makes the function fully open. This is more critical
than for Tier 2 functions, where the gateway provides a backup layer.
For service-role functions (Tier 2), in-code auth is defense-in-depth on top
of gateway verification. Both should be present.
Deployment: Lovable does NOT auto-deploy external changes
Edge functions added or modified by Claude Code or engineers are not
automatically deployed by Lovable. After merging a PR that adds, modifies,
or changes config.toml for edge functions, the user must explicitly ask
Lovable to deploy them. This applies to:
- New edge functions (code + config.toml entry)
- Modified edge function code
verify_jwt changes in config.toml
Tell the user to send Lovable: "Deploy these edge functions: <name1>, <name2>"
Without this step, the function code is in the repo but returns 404 (new
functions) or runs with stale code/config (modified functions).