Clerk
This guide explains how to connect your Supabase database with Clerk, an authentication provider built for the modern web.
Clerk authenticates users, manages session tokens, and provides user management functionality that can be used in combination with the authorization logic available in Supabase through PostgreSQL Row Level Security (RLS) policies.
This guide assumes you have a Supabase account and database project already set up.
If you don't have a Clerk account, you can create one now.
Step 1: Create JWT template#
The first step is to create a new Clerk application from your Clerk Dashboard if you haven't done so already. You can choose whichever authentication strategy and social login providers you prefer. For more information, check out Clerk's guide.
After your Clerk application has been created, use the lefthand menu to navigate to the JWT Templates page.
Click on the button to create a new template based on Supabase.
This will pre-populate the default claims required by Supabase. You can include additional claims or modify them as necessary. Shortcodes are also available for adding dynamic values.
ℹ️ Note the name of the JWT template (which you can change) because this will be needed later.
Step 2: Sign JWT with Supabase secret#
Supabase requires JWTs be signed with the HS256 signing algorithm and use their signing key. Find the JWT secret key in your Supabase project under Settings > API in the Config section.
Click to reveal the JWT secret, copy it, and then paste it in the Signing key field in the Clerk JWT template.
After the key is added, click the Apply Changes button to save your template.
Step 3: Configure client#
The next step is to configure your client. Supabase provides an official JavaScript/TypeScript client library and there are libraries in other languages built by the community.
This guide will use a Next.js project with the JS client as an example, but the mechanism of setting the authentication token should be similar with other libraries and frameworks.
Assuming a Next.js application, set the following environment variables in an .env.local
file:
_10NEXT_PUBLIC_CLERK_FRONTEND_API=your-frontend-api_10NEXT_PUBLIC_SUPABASE_URL=your-supabase-url_10NEXT_PUBLIC_SUPABASE_KEY=your-supabase-anon-key
Note: If you're using Create React App, replace the NEXT_PUBLIC
prefix with REACT_APP
Your Clerk Frontend API can be found on the API Keys screen.
To get the ones needed for Supabase, navigate to the same Settings > API page as before and locate the anon public key and URL.
Note: It is recommended that you enable Row Level Security (RLS) for your database tables and configure access policies as needed.
After setting those three environment variables, you should be able to start up your application development server.
Install the JavaScript client for Supabase with:
_10npm install @supabase/supabase-js
Initialize the Supabase client by passing it the environment variables.
This can be saved to a common file, for example as lib/supabaseClient.js
_10import { createClient } from '@supabase/supabase-js'_10_10const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL_10const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY_10_10export const supabase = createClient(supabaseUrl, supabaseKey)_10export default supabase
Step 4: Set up Clerk Provider#
Install the latest Clerk Next.js SDK by running the following:
_10npm install @clerk/nextjs@next
Note: There is also a Clerk library for React and React Native with Expo.
After the package is installed, wrap your application with the <ClerkProvider />
component.
In a Next.js application, this is typically done in pages/_app.js
:
_11import { ClerkProvider } from '@clerk/nextjs'_11_11function MyApp({ Component, pageProps }) {_11 return (_11 <ClerkProvider>_11 <Component {...pageProps} />_11 </ClerkProvider>_11 )_11}_11_11export default MyApp
Step 5: Set auth token with Supabase#
In order to access the custom JWT, you can use the getToken
function returned by the Clerk useAuth
hook and pass it the name of your template (hopefully you remembered from earlier).
Note: The getToken({ template: <your-template-name> })
call is asynchronous and returns a Promise that needs to be resolved before accessing the token value. This token is short-lived for better security and should be called before every request to your Supabase backend. The caching and refreshing of the token is handled automatically by Clerk.
Call supabase.auth.setAuth(token)
to override the JWT on the current client. The JWT will then be sent to Supabase with all subsequent network requests.
_24import { useAuth } from '@clerk/nextjs'_24import supabase from '../lib/supabaseClient'_24_24export default function Home() {_24 const { getToken } = useAuth()_24_24 const fetchData = async () => {_24 // TODO #1: Replace with your JWT template name_24 const token = await getToken({ template: 'supabase' })_24_24 supabase.auth.setAuth(token)_24_24 // TODO #2: Replace with your database table name_24 const { data, error } = await supabase.from('your_table').select()_24_24 // TODO #3: Handle the response_24 }_24_24 return (_24 <button type="button" onClick={fetchData}>_24 Fetch data_24 </button>_24 )_24}
Access user ID in RLS policies#
It is common practice to need access to the user identifier on the database level, especially when working with RLS policies in Postgres. Although Supabase provides a special function auth.uid()
to extract the user ID from the JWT, this does not work with Clerk. The workaround is to write a custom SQL function to read the sub
property from the JWT claims.
In the SQL Editor section of the Supabase dashboard, click New Query and enter the following:
_10create or replace function requesting_user_id()_10returns text_10language sql stable_10as $$_10 select nullif(current_setting('request.jwt.claims', true)::json->>'sub', '')::text;_10$$;
This will create a requesting_user_id()
function that can be used within an RLS policy.
For example, this policy would check that the user making the request is authenticated and matches the user_id
column of a todos table.
Access user ID in table column#
If you would like the requesting user ID from the JWT to automatically populate a text type column in your database table, you can set the Default Value field to the previously defined requesting_user_id()
function.