Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions docs/integrations/databases/supabase-and-orgs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: Support organizations in your Clerk + Supabase application
description: Learn how to integrate Clerk and support Clerk organizations in your Supabase application.
---

<TutorialHero
beforeYouStart={[
{
title: "Integrate Supabase with Clerk",
link: "/docs/integrations/databases/supabase",
icon: "code-bracket",
},
]}
exampleRepo={[
{
title: "Supabase, Next.js, and Clerk Demo",
link: "(TODO)",
icon: "github",
},
]}
/>

Clerk [organizations](/docs/organizations/overview) allow you to group users together and assign them specific roles and permissions, giving you a flexible and scalable way to manage users and their access to resources within your application.

This guide will show you how to support Clerk organizations in your Clerk + Supabase application. If you haven't integrated Clerk with Supabase yet, follow the steps in the [guide on integrating Supabase with Clerk](/docs/integrations/databases/supabase). This guide is a direct extension of that guide.

## Understand the Clerk JWT

Supabase identifies the party making a request by parsing the claims of the incoming JWT. Clerk's JWT, also referred to as the [session token](/docs/backend-requests/resources/session-tokens), contains the `o.id` claim, which stands for the organization ID. This is the value that Supabase uses to identify the organization the user belongs to.

The following is an example of a Clerk session token for a user in an organization, and that organization is [active](/docs/organizations/overview#active-organization). Notice the `o` claim which contains properties that identify the organization, such as the `id` and `slg` (slug) properties.

```js {{ prettier: false }}
{
azp: 'http://localhost:3001',
email: '[email protected]',
exp: 1744734948,
fea: 'o:custom-feature',
fva: [ 0, -1 ],
iat: 1744734888,
iss: 'https://renewing-bobcat-00.clerk.accounts.dev',
jti: '004f0096e5cd44911924',
nbf: 1744734878,
o: {
fpm: '1',
id: 'org_123',
per: 'custom-perm',
rol: 'admin',
slg: 'example-org'
},
role: 'authenticated',
sid: 'sess_123',
sub: 'user_123',
v: 2
}
```

To learn about each of the claims and what they mean, see the [guide on session tokens](/docs/backend-requests/resources/session-tokens).

Now that you're familiar with the Clerk JWT, let's support Clerk organizations in your Supabase application.

<Steps>
## Enable organizations in the Clerk Dashboard

Organizations are disabled by default. To enable organizations:

1. In the Clerk Dashboard, navigate to the [**Organizations Settings**](https://dashboard.clerk.com/last-active?path=organizations-settings) page.
1. Select **Enable Organizations**.

## Add `<OrganizationSwitcher />` to your app

The [`<OrganizationSwitcher />`](/docs/components/organization/organization-switcher) component allows a user to switch between their account types - their personal account and their joined organizations. It handles all organization-related flows, including full organization management for users with the correct permissions.

Add the `<OrganizationSwitcher />` component to your app. In the following example, it's added to the header of the app, besides the `<UserButton />` component.

```tsx {{ filename: 'app/layout.tsx', mark: [3, 21] }}
import {
ClerkProvider,
OrganizationSwitcher,
SignInButton,
SignedIn,
SignedOut,
UserButton,
} from '@clerk/nextjs'
import './globals.css'

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html lang="en">
<body>
<header>
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>
<OrganizationSwitcher />
<UserButton />
</SignedIn>
</header>
<main>{children}</main>
</body>
</html>
</ClerkProvider>
)
}
```

## Update your `task` table to include the organization ID

The `task` table needs to include the organization ID in order to support organizations. In the [Supabase SQL editor](https://supabase.com/dashboard/project/_/sql/new), run the following command to add the `org_id` column to the `tasks` table:

```sql
-- Add org_id column to tasks table
ALTER TABLE tasks
ADD COLUMN org_id text DEFAULT auth.jwt()->>'o.id';
```

## Update your RLS policies to include the organization ID

You can access Clerk session token data in Supabase using the built-in `auth.jwt()` function. In the [guide on integrating Supabase with Clerk](/docs/integrations/databases/supabase), you learned how to use this function to extract the `sub` value (the user ID) from the JWT claims of the user making the request. This same function can be used to access the `o.id` value as well.

Let's update the RLS policies to include the organization ID where users can view and create tasks when either:

- They are the owner of the task (`user_id` matches) and there's no active organization
- The task belongs to their active organization (`org_id` matches)

You'll need to run a migration to update the RLS policies.

1. To run migrations, you'll need to install the Supabase CLI and start the local development stack. See the [Supabase docs](https://supabase.com/docs/guides/local-development#quickstart) for instructions.
1. Add the following SQL commands to the migration file:
```sql
-- Update RLS policies to include organization ID
drop policy if exists "User can view their own tasks" on tasks;
drop policy if exists "Users must insert their own tasks" on tasks;

-- Create new policies that check both user_id and org_id
create policy "Users can view their own tasks"
on tasks
for select
using (
(auth.jwt()->>'sub' = user_id and auth.jwt()->>'o.id' is null) or
(auth.jwt()->>'o.id' = org_id)
);

create policy "Users must insert their own tasks"
on tasks
for insert
with check (
(auth.jwt()->>'sub' = user_id and auth.jwt()->>'o.id' is null) or
(auth.jwt()->>'o.id' = org_id)
);
```
1. Apply the migration to the database by executing the following command in your terminal:

> [!IMPORTANT]
> You may need to link your Supabase project to the CLI. You can do so by running `npx supabase link`. It may need you to sign in to your Supabase account. You can do so by running `npx supabase login`, which will open a browser window to Supabase and give you a code to paste into the CLI in order to sign in. Then you will need to run `npx supabase link` again to link the project to the CLI.

```bash
npx supabase db push
```

## Test your changes

1. Run your project and sign in. Create a task; this one will be visible if you don't have an active organization.
1. The `<OrganizationSwitcher />` component will say "Personal account" which indicates that there is no [active organization](/docs/organizations/overview#active-organization). Select the component and create an organization.
1. Your list of tasks should now be empty (TODO: this may not be true, I think the non-org tasks may still be visible, but I need to test once the migration TODO is done), because the "Users can view their own tasks" policy....
1. Create a new task.
1. To verify that the new task is only visible to users in the organization, use the `<OrganizationSwitcher />` component to invite a new user to the organization.
1. Sign out and sign in as the new user. Use the `<OrganizationSwitcher />` component to verify that the user is part of the organization, and that the organization is set as active. The new task should be visible.
</Steps>
4 changes: 4 additions & 0 deletions docs/integrations/databases/supabase.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ This guide will have you create a new table in your [Supabase project](https://s
Run your project and sign in. Test creating and viewing tasks. Sign out and sign in as a different user, and repeat.

If you have the same tasks across multiple accounts, double check that RLS is enabled, or that the RLS policies were properly created. Check the table in the Supabase dashboard. You should see all the tasks between both users, but with differing values in the `user_id` column.

## Clerk organizations

If you use Clerk organizations, see the guide on [integrating Supabase with Clerk organizations](/docs/integrations/databases/supabase-and-orgs).
</Steps>

## What does the Clerk Supabase integration do?
Expand Down
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3309,6 +3309,10 @@
"title": "Supabase",
"href": "/docs/integrations/databases/supabase"
},
{
"title": "Supabase and Clerk Organizations",
"href": "/docs/integrations/databases/supabase-and-orgs"
},
{
"title": "Neon",
"href": "/docs/integrations/databases/neon"
Expand Down
8 changes: 8 additions & 0 deletions supabase/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Supabase
.branches
.temp

# dotenvx
.env.keys
.env.local
.env.*.local
Loading