Handling user authentication and connecting to a database can be tough, especially with modern web apps. Clerk and Supabase are two great tools that make this process easier, providing smooth user management and scalable database options. In this article, we’ll learn, how to connect Clerk's authentication system with Supabase to build a strong and secure user management process.
So, without wasting a bit, let’s get started.
Initial Setup
The first thing that, we'll do is set up our Next.js project and install the necessary dependencies.
Install Dependencies
Run the following command to install Next.js on your machine.
npx create-next-app@latest
On installation, you'll see the following prompts:
What is your project named? my-app
Would you like to use TypeScript? No / Yes => I picked no
Would you like to use ESLint? No / Yes => I picked yes
Would you like to use Tailwind CSS? No / Yes => I picked yes
Would you like your code inside a `src/` directory? No / Yes => I picked no
Would you like to use App Router? (recommended) No / Yes => I picked yes
Would you like to use Turbopack for `next dev`? No / Yes => I picked no
Would you like to customize the import alias (`@/*` by default)? No / Yes => I picked no
What import alias would you like configured? @/* => I picked @
Our next js has been installed, now let’s install clerk and supabase. Run this command to install clerk👇
npm i @clerk/nextjs
and this one to install supabase👇
npm i @supabase/supabase-js
We've installed the necessary dependencies, so now let's set up the Supabase and Clerk dashboards to ensure they work together.
Setup Clerk Dashboard
Click here to sign up for Clerk or sign in to your existing Clerk account.
If you've just created an account for the first time, you'll go straight to the authentication setup form.
If not, you'll be sent to your Clerk Dashboard. To make a new app, click on the Create application card to access the authentication setup form. Fill in all the details and also select social auth providers like Google, Github, Facebook, etc. whatever you want.
It will create your new clerk application on which you can start adding your users with their auth flow. Your dashboard will look like this. 👇
That’s it, clerk setup is done now let’s set up Supabase.
Setup Supabase Dashboard
Go to this link to sign in to your Supabase dashboard.
Now, go to this link to create a new Supabase project. Once your project is created, it should be visible in your dashboard like this.
Our supabase dashboard is also set. Now, let’s see the integration part.
Integrate Clerk
We've installed all our dependencies and set up both dashboards. Now, we'll integrate Clerk and Supabase into our app, one by one. We'll start with Clerk first, then move on to Supabase.
Before moving forward, let's look at the folder structure. I have added a few new files and folders that we will use later in this article.
Set Clerk Provider
The first thing we need to do is wrap our app in a <ClerkProvider />
so it can manage our auth state from the top of the hierarchy. The <ClerkProvider>
component wraps your app to provide active session and user context to Clerk's hooks and other components.
Head over to your layout.js
file, and wrap your main component into <ClerkProvider/>
👇
export default function RootLayout({ children }) {
return (
<ClerkProvider touchSession={false} >
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable}
antialiased`}
>
{children}
</body>
</html>
</ClerkProvider>
);
}
ClerkProvider
has several props. As you can see, I’ve added one prop called touchSession
. By default, it's true
, but you can set it to false
. When it's false
, it won't check the user session on every click. Otherwise, it will call an API each time to see if the user’s session is active. For a full list of ClerkProvider
props, you can check it here.
Now, let's add sign-in and sign-up buttons. Clerk provides its own UI and components to make this process easier, like this 👇
import localFont from 'next/font/local';
import './globals.css';
import {
ClerkProvider,
SignedIn,
SignedOut,
SignInButton,
UserButton,
} from '@clerk/nextjs';
// rest of your code
export default function RootLayout({ children }) {
return (
<ClerkProvider touchSession={false}>
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<header> // here you can use styling to style it a/c to your design
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
{children}
</body>
</html>
</ClerkProvider>
);
}
That’s it, now let’s set up env variables.
Setup clerk env
In the Clerk Dashboard, navigate to the Configure > API Keys page.
In the Quick Copy section, copy your Clerk publishable and secret key.
Paste your keys into your .env.local
file.
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=clerk_publishable_key
CLERK_SECRET_KEY=thiswillbeyoursecretkey
Middleware Setup
The last important step is to set up the middleware.
Middleware helps us protect our routes. For example, if we have three routes named login
, dashboard
, and about
, and we don't want users to access the dashboard
and about
without logging in, auth middleware can help us with this.
For Clerk, we'll set up our middleware based on our route structure. Create a middleware.js
file in your root folder and add this code 👇
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
const isProtectedRoute = createRouteMatcher(['/user(.*)'])
const isPublicRoute = createRouteMatcher(['/'])
export default clerkMiddleware(async (auth, req) => {
const { userId } = await auth()
if (userId && req.nextUrl.pathname === '/') {
const userUrl = new URL('/user', req.url)
return NextResponse.redirect(userUrl)
}
if (isProtetedRoute(req)) {
await auth.protect()
}
})
Here, we're indicating that /user
routes are protected. So, if any user tries to access this route without logging in, they will first be redirected to the /
route to sign in or sign up.
The Clerk setup is complete. You can test your sign-in and sign-up process, and all your users will be visible on your Clerk dashboard.
Integrate Supabase
Now that we have our authentication flow set up, it's time to save our logged-in users in our Supabase database. When a user signs up in your app using the Clerk provider, Clerk will generate an authentication token and send it to Supabase using the Supabase project's JWT secret key.
You'll provide the Supabase JWT secret key to Clerk, and Clerk will share the auth token with Supabase. With this connection, you'll be able to save your user's unique user_id
. After that, we can perform any CRUD operations.
Create Function
First, we'll create a function in our Supabase to request the user ID from Clerk. Then, we'll pass our Supabase JWT to Clerk, to connect them.
In the sidebar of your Supabase dashboard, go to Database > Functions.
Click on the Create New Function button 👇
Enter function name
Return type text
and add this definition
SELECT NULLIF(
current_setting('request.jwt.claims', true)::json->>'sub',
''
)::text;
Scroll a bit, and turn on the advanced settings option
select language sql
Click Confirm
to save it.
Next, create a user_id
column in the table you want to secure. This column will be used in the RLS policies to only return or modify records related to the user's account. It will use the requesting_user_id_from_clerk()
function we just created as its default value.
Go to the sidebar on the left and select Table Editor
.
Select the table you wish to secure.
In the table, select the + column to add a new column.
Set the Name to user_id
.
Set Type to text.
Set Default Value to requesting_user_id_from_clerk()
.
Select Save to create the column.
Make sure your RLS policy is enabled for that particular table.
In the top bar above the table, you can see the options to Enable RLS, Disable RLS, or Add New RLS Policy. Currently, I have one auth policy, so it shows 1 here.
Here you can see all your policies, click on the Create New Policy button
In this way, you can create an RLS policy for insert, delete, view, and update actions. As you can see, this RLS policy is for insert, and we are targeting public roles (point 4 in the screenshot).
Similarly, you can do the same for SELECT
authenticated roles. like this 👇
Same for the UPDATE
. This time, without check expression.👇
To learn more about RLS Policies, you can check out this guide of supabase.
Get JWT Token
Supabase's API requires an authentication token to allow users to access your data. Your Clerk project can generate these tokens, but it first needs the JWT secret key from your Supabase project. As we discussed earlier.
To get JWT Secret:
- Go to Settings from the sidebar
- Then click on the API Section.
- Then, click on
Reveal
and copy the JWT Secret.
Now that we've finished the Supabase part, we'll create a Supabase JWT template on Clerk and integrate the JWT secret there.
Clerk JWT Template Setup
Go to your clerk dashboard, and head over to Clerk JWT Template. Click on the new template option.
Select Supabase.
Fill out 2 details here:
- Name: If you have multiple Clerk projects and multiple JWT Templates for each project, make sure to give this name a slight difference from the others.
- Then, paste your Supabase JWT Key into the signing key box below.
Now click save.
Great, now we have done almost everything important. Two things are left: setting up the environment for Supabase and integrating the Supabase client into our application. Then, we'll be ready to go.
Supabase Client Setup
Create a new folder in your root directory named lib
and inside that create a file supabaseClient.js
And, add this code to it
import { createClient } from '@supabase/supabase-js';
// Function to create a Supabase client with Clerk authentication
export function createClerkSupabaseClient(session) {
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_KEY,
{
global: {
fetch: async (url, options = {}) => {
const clerkToken = await session?.getToken({
template: "enter-your-jwt-clerk-template-name",
});
const headers = new Headers(options?.headers);
headers.set("Authorization", `Bearer ${clerkToken}`);
return fetch(url, { ...options, headers });
},
},
}
);
}
Here, we have two envs
, so we'll add these to our .env.local
file. Go to Settings > API and copy the project URL and anon key.
and paste them here in .env.local with clerk’s env variables.
NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_KEY=""
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
CLERK_SECRET_KEY=""
And, that’s it. We are good to go. Now you can run the server using yarn, npm, or pnpm. Whatever you choose. I am using npm.👇
npm run dev
Once the server starts, you can click on the Sign In button to log in. Without signing in, you won't be able to access /user/dashboard
and /user/about
, as we have set this restriction in our middleware.js
file.
Conclusion
That’s it for today. In this article, we learn about how we can integrate Clerk's authentication system with Supabase in a Next.js app to build a secure user management process. We covered setting up the Next.js environment, installing necessary dependencies, configuring the Clerk and Supabase dashboards, implementing authentication, and authorization using middleware.
I hope you learn something from this blog, make sure to share it with your friends and community, and if you learn while reading it, make sure to give it a like and leave a comment. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one.
BYE 👋
Useful Links:
Clerk get started guide: https://clerk.com/docs/quickstarts/setup-clerk
Supabase get started guide: https://supabase.com/docs/guides/getting-started
Clerk + Supabase Guide: https://clerk.com/docs/integrations/databases/supabase
Supabase + Clerk Guide: https://supabase.com/partners/integrations/clerk
Top comments (0)