Files
supabase/apps/docs/content/guides/integrations/partner-integration-guide.mdx
Miranda Limonczenko 608040b8cb chore(docs) Resolve 'simple' style warnings where applicable (#46966)
Contributes to DOCS-1052

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Resolves MDX linting errors related to "simple" where it applies.
There was a couple cases that did not apply. For example, a product with
"Simple" in the name.

These changes are made in context, either by removing or using a more
descriptive synonym like "minimal" or "basic".

## Tophatting

1. Read each of the diffs.
2. See that the text still makes sense in context.

For extra due diligence, you can run `pnpm lint:mdx` locally and see the
'simple' errors that remain and whether they are worth addressing.


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

## Summary by CodeRabbit

* **Documentation**
* Updated many guide, tutorial, and troubleshooting pages with clearer
“basic”/“minimal” wording across setup steps, local testing
instructions, security cautions, and RLS guidance.
* Refined headings, example descriptions, and inline comments for
consistency (including deployment, MCP, metrics API, and search/function
phrasing).
* Improved readability with small snippet formatting tweaks (whitespace
plus import/comment ordering) and added a self-hosting debugging note
for Envoy admin endpoints via a short-lived `curl` container.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>
Co-authored-by: Nik Richers <nrichers@gmail.com>
2026-06-16 21:45:55 +00:00

277 lines
12 KiB
Plaintext

---
id: 'partner-integration-guide'
title: 'Supabase Partner Integration Guide'
subtitle: 'Integrate Supabase with your platform or product.'
description: 'This guide explains how a Supabase partner can integrate with the Supabase platform via OAuth.'
---
This guide assumes you have already followed [the Build a Supabase Integration guide](/docs/guides/integrations/build-a-supabase-oauth-integration) and have a working OAuth client.
When a Supabase user clicks the **Install Integration** button in the Dashboard, they are redirected to your system to begin the OAuth flow. You can implement this redirect in one of two ways:
- **[Redirect](#method-1-redirect)**: Easier to build, but your system cannot verify that the incoming user was sent by Supabase.
- **[Signed redirect](#method-2-signed-redirect)**: More work to build, but cryptographically verifies that the redirect originated from Supabase. **Recommended for production integrations.**
Pick the method that fits your security requirements, then follow the matching section below.
## Method 1: Redirect
In this method, you implement a single `GET` endpoint. Supabase redirects the user to this endpoint when they click **Install Integration**, and your endpoint kicks off the OAuth flow.
```mermaid
sequenceDiagram
autonumber
participant User
participant Browser
participant Partner as Partner (OAuth Client)
participant Supabase as Supabase (OAuth Authorization Server/Protected Resource)
Note over Browser: Supabase Dashboard Open
User->>Browser: Clicks "Install Integration"
Browser->>Partner: "Connect to Partner" Click Handler Page
Partner->>Browser: Redirect to Supabase Authorization Page
Browser->>Supabase: Request Authorization
Supabase->>Browser: Redirect to Consent Screen
Browser->>User: Display Consent Screen
User->>Browser: Gives Consent
Browser->>Supabase: Submit User Consent
Supabase->>Browser: Redirect to Partner With Authorization Code
Browser->>Partner: Submit Authorization Code
Partner->>Supabase: Exchange Authorization Code for Token
Supabase->>Partner: Return Token
Partner->>Supabase: Request Management API Resources
Supabase->>Partner: Return Resources
Partner->>Browser: Ok 200, OAuth Flow Complete
Browser->>User: Display "Integration Installed" Message
```
### Step 1: Implement the redirect endpoint
Expose a `GET` endpoint at any URL you control:
```
GET https://<your-host>/<optional-path>?project_id=<supabase-project_ref>&organization_slug=<supabase-org-slug>
```
Supabase will append the following query parameters to the URL when redirecting:
| Param | Description |
| ------------------- | ---------------------------------------------------------------------------------------------- |
| `project_id` | Supabase project ref of the project where the user clicked the **Install Integration** button. |
| `organization_slug` | Supabase organization slug where the user clicked the **Install Integration** button. |
Save these parameters in your system. You can use them to fetch project or organization details, or to pre-select a project or organization in your UI once the OAuth flow is complete.
Your endpoint may ask the user to sign up, sign in, or perform other setup tasks on your website. Once those are complete, immediately [redirect to the Supabase authorization URL](/docs/guides/integrations/build-a-supabase-oauth-integration#redirecting-to-the-authorize-url) without further user interaction. This starts the OAuth flow.
### Step 2: Share your endpoint with Supabase
Send Supabase the URL of your endpoint so we can configure the **Install Integration** button to redirect there.
## Method 2: Signed redirect
In this method, Supabase signs a JWT before redirecting the user. You verify the signature, generate a one-time redirect record, and return its URL to Supabase. Supabase then redirects the user to that URL. This guarantees that any redirect your system handles originated from Supabase.
You will implement two endpoints:
1. A `POST` endpoint that validates the signed JWT and returns a unique redirect URL.
2. A `GET` endpoint that handles the user once they are redirected to that URL.
```mermaid
sequenceDiagram
autonumber
participant User
participant Browser
participant Partner as Partner (OAuth Client)
participant Supabase as Supabase (OAuth Authorization Server/Protected Resource)
Note over Browser: Supabase Dashboard Open
User->>Browser: Clicks "Install Integration"
Browser->>Supabase: Request Redirect URL Generation
Supabase->>Partner: Send Signed JWT
Partner->>Partner: Validate JWT
Partner->>Partner: Generate Unique Redirect URL
Partner->>Supabase: Return Redirect URL
Supabase->>Browser: Redirect to the Redirect URL
Browser->>Partner: Visit Redirect URL
Partner->>Partner: Validate Redirect Record
Partner->>Browser: Optional User Actions
Browser->>User: User Performs Optional Actions
User->>Browser: User Optional Actions Complete
Browser->>Partner: User Optional Actions Complete
Partner->>Browser: Redirect to Supabase Authorization Page
Browser->>Supabase: Request Authorization
Supabase->>Browser: Redirect to Consent Screen
Browser->>User: Display Consent Screen
User->>Browser: Gives Consent
Browser->>Supabase: Submit User Consent
Supabase->>Browser: Redirect to Partner With Authorization Code
Browser->>Partner: Submit Authorization Code
Partner->>Supabase: Exchange Authorization Code for Token
Supabase->>Partner: Return Token
Partner->>Supabase: Request Management API Resources
Supabase->>Partner: Return Resources
Partner->>Browser: Ok 200, OAuth Flow Complete
Browser->>User: Display "Integration Installed" Message
```
### Step 1: Exchange public keys with Supabase
Supabase generates two key-pairs, one for staging, one for production, and shares the public keys with you. Save both public keys and their key IDs in your system. The keys are PEM-encoded EC P-256.
Example:
```
===============Staging==================
Key ID: pik_3038669348a3ea75dbaf0655
Public Key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnj3NmwrLPPH/3isvpS601ndQP9Mk
zqppdLDV9YfmoF4wavTyb9UTVE5pJ0fukpo5aOoNb4fBZgESsedIUoEn8Q==
-----END PUBLIC KEY-----
==============Production================
Key ID: pik_89e80ddbca9df41b97e28986
Public Key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWCGhwtFWn4jpWZNeyZpTlaAdq/tD
/yBaN0gFPpS8LTFiCPFgnWKbVe3RfExXh7bEhcrEUdWycmYvwrNklWWHRA==
-----END PUBLIC KEY-----
```
Look up the correct public key by the `kid` field in the JWT header.
Storing keys by ID enables zero-downtime key rotation. When Supabase rotates keys, we share new key-pairs and start signing JWTs with the new key ID. Your system picks the correct key based on `kid` without any code changes.
<Admonition type="note">
In future, we plan to publish keys at a `.../.well-known/jwks.json` URL and fully automate key rotation. For now this is done manually.
</Admonition>
### Step 2: Implement the redirect record endpoint
This endpoint receives the signed JWT from Supabase and returns a one-time redirect URL.
Host the endpoint at any URL and path you control. Do not require authentication on this endpoint, but apply rate limiting to prevent abuse.
```
POST https://<your-api-host>/<your-api-path>
Content-Type: application/json
```
#### Request body
```json
{
"token": "<signed-jwt>"
}
```
#### JWT fields
The token is a JWT signed with the ES256 private key.
**Protected header (JOSE header)**
| Field | Required | Description |
| ----- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `alg` | Yes | Always `ES256` — the only signing algorithm currently supported. |
| `kid` | Yes | The key ID identifying the key pair. Use this to pick the correct public key when verifying the signature. |
**Payload (claims)**
| Claim | Required | Description |
| ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `iss` | Yes | Always `supabase`. |
| `aud` | Yes | A unique string identifying the audience of this JWT. Usually a URL. |
| `iat` | Yes | Issued-at timestamp (seconds since epoch). |
| `exp` | Yes | Expiry timestamp — at most 5 minutes after `iat`. |
| `organization_slug` | No | The Supabase organization the user is connecting from. Use to pre-select the org during the OAuth consent screen. |
| `project_id` | No | The Supabase project ref the user wants to connect. Use to pre-select a Supabase project in your UI. |
**Signature**
The header and claims are signed with the EC P-256 private key.
#### Validation
When you receive a request:
1. Read the `kid` field from the JWT header and look up the matching public key.
2. Verify the JWT signature with that public key.
3. Verify that `alg` is `ES256`.
4. Verify that `iss` is `supabase`.
5. Verify that `aud` matches the value you agreed with Supabase.
6. Verify that the current time is between `iat` and `exp`.
If any check fails, return `401 Unauthorized`.
If validation succeeds, generate a UUID to identify this redirect record (also called an integration record), save it in your system with an expiry (typically 1 hour), and return it in the response. The expiry prevents records from accumulating and limits the window in which a leaked record can be used.
#### Response body
```json
{
"integrationId": "<a unique uuid>",
"redirectUrl": "https://<your-api-host>/<your-api-path>/<integration-id>",
"expiresAt": "<timestamp>"
}
```
| Field | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `integrationId` | A UUID uniquely identifying the redirect record. |
| `redirectUrl` | The URL the user will be redirected to. Must contain the `integrationId` somewhere in its path so you can retrieve the record on redirect. |
| `expiresAt` | The time when the redirect record expires (typically 1 hour from creation). The user must begin the flow before this time. |
### Step 3: Implement the redirect handler endpoint
This is the `GET` endpoint at the `redirectUrl` you returned in the previous step. The redirect record's UUID must be in its path.
When a user arrives at this endpoint:
1. Extract the redirect record UUID from the URL path.
2. Look it up in your system. If it doesn't exist or has expired, return `401 Unauthorized`.
3. Optionally, walk the user through any setup required on your side — for example, signing up, signing in, or configuring your system so the integration will work.
4. Redirect the user to the [Supabase authorization URL](/docs/guides/integrations/build-a-supabase-oauth-integration#redirecting-to-the-authorize-url) to start the OAuth flow.
<Admonition type="tip">
If you need the user to perform setup steps on your site, design the experience as a wizard that ends by redirecting to the Supabase authorization URL. This minimizes the chance of users getting distracted, navigating elsewhere on your site, and abandoning the OAuth flow.
</Admonition>
### Step 4: Share your details with Supabase
Send Supabase the following so we can configure the **Install Integration** button:
- The URL of your redirect record endpoint ([Step 2](#step-2-implement-the-redirect-record-endpoint)).
- The URL pattern of your redirect handler endpoint ([Step 3](#step-3-implement-the-redirect-handler-endpoint)).
- The `aud` claim value you want Supabase to send in the signed JWT.
Ask Supabase for the public keys and key IDs for the staging and production environments.