โกSupabase
A guide to allowing users to login with their wallet to your Supabase-powered app!
Live Demo
Checkout a live demo of a Supabase + Picket powered app
Overview
Supabase is an open-source firebase alternative. Supabase's JWT-based authentication makes it easy to plug in additional authentication providers, like Picket. Picket's Supabase integration gives you the best of web2 and web3. Picket allows your users to log in with their wallet, but still leverage Supabase's awesome data management and security features.
Use Cases for Supabase + Picket
Account linking. Allow users to associate their wallet address(es) with their existing web2 account in your app
Leverage Supabase's awesome libraries and ecosystem while still enabling wallet login
Store app-specific data, like user preferences, about your user's wallet adress off-chain
Cache on-chain data to improve your DApp's performance
This guide will walk you through how to add Picket to a new Supabase app. By the end of this guide your users will be able to log into your Supabase app via their wallet of choice!
Getting Started
Requirements
You have a Picket account. If you don't, sign up at https://picketapi.com/
You've read the Setup Guide
You have Supabase account. If you don't, sign up at https://supabase.com/
Setup Picket
First, we'll create a new project in our Picket dashboard.
1. Go to your Picket Dashboard
2. Create a new Project
You'll see the Create New Project
button at the top of the Projects section of your Picket dashboard. Alternatively, you can re-use an existing project. Feel free to edit the project to give it a memorable name.
We're done for now! We'll revisit this project when we are setting up environment variables in our app.
Setup Supabase
Next, we'll create and initialize an equivalent project in Supabase.
1. Create a New Project
From your Supabase dashboard, click New project
.
Enter a Name
for your Supabase project.
Enter a secure Database Password
.
Click Create new project
.
2. Create a New Table
From the sidebar menu in the Supabase dashboard, click Table editor
, then New table
.
Enter todos
as the Name
field.
Select Enable Row Level Security (RLS)
.
Create four columns:
name
astext
wallet_address
astext
completed
asbool
with the default valuefalse
created_at
astimestamptz
with a default value ofnow()
Click Save
to create the new table.
3. Setup Row Level Security (RLS)
Now we want to make sure that only the todos
owner, the user's wallet_address
, can access their todos. The key component of the this RLS policy is the expression
This expression checks that the wallet address in the requesting JWT access token is the same as the wallet_address
in the todos
table.
Create a Next.js App
Now, let's start building!
Create a new Typescript Next.js app
Create a .env.local
file and enter the following values
NEXT_PUBLIC_PICKET_PUBLISHABLE_KEY
=> Copy the publishable key from the Picket project you created in the previous stepPICKET_PROJECT_SECRET_KEY
=> Copy the secret key from the Picket project you created in the previous stepNEXT_PUBLIC_SUPABASE_URL
=> You can find this URL under "Settings > API" in your Supabase projectNEXT_PUBLIC_SUPABASE_ANON_KEY
=> You can find this project API key under "Settings > API" in your Supabase projectSUAPBASE_JWT_SECRET
=> You can find this secret under "Settings > API" in your Supabase project
Setup Picket for Wallet Login
Picket React Quick Start Guide
For more information on how to setup Picket in your React app, checkout the getting started guide
After initializing our app, we can setup Picket.
Install the Picket React and Node libraries
Update pages/_app.tsx
to setup the PicketProvider
Update pages/index.tsx
to let users log in and out with their wallet
Issue a Supabase JWT on Login
Great, now we have setup a typical Picket React app. Next, we need to implement the log in/out API routes to allow users to securely query our Supabase project.
First, install dependencies
Create a utility function to create a Supabase client utils/supabase.ts
Create new api route pages/api/login.ts
. This route validates the Picket access token then issues another equivalent Supabase access token for us to use with the Supabase client.
And now create an equivalent logout api route /pages/api/logout.ts
to delete the Supabase access token cookie.
We can now login and logout to the app with our wallet!
Interacting with Data in Supabase
Now that we can login to the app, it's time to start interacting with Supabase. Let's make a todo list page for authenticated users.
Create a new file pages/todos.tsx
This is a long file, but don't be intimidated. The page is actually straightforward. It
Verifies server-side that the user is authenticated and if they are not redirects them to the homepage
Checks to see if they already have
todos
. If so, it returns them. If not, it initializes them for the usersWe render the
todos
and when the user selects or deselects a todo, we update the data in the database
Try it Out!
And that's it. If you haven't already, run your app to test it out yourself
[Advanced Guide] Supporting Multiple Login Methods
At this point, you have a Supabase-powered app that allows users to log in with their wallets. This works well for apps that only allow users to connect and login with their wallet. But what if you want allow users to log in with their wallet or traditional authentication mechanisms like email, phone, or OAuth 2.0.
This guide walks you through how to integrate wallet login into Supabase's native auth.users
table. By leveraging Supabase's native auth table, users can log in via their preferred authentication method. Downstream of login, you can treat all users the same regardless of their login method by referencing their unique user id
from the auth.users
table.
Tailor to Your App's Requirements
It's important to note that this guide is a template for using Picket to enable wallet login with Supabase's native auth.users
table. It is not meant to be prescriptive. Treat this guide as a starting point for your application and customize it to meet your applications specific requirements. If you have any questions, don't hesitate to reach out to team@picketapi.com
Update Login Endpoint
First, we are going to update the login endpoint pages/api/login.ts
to fetch the corresponding Supabase user for the wallet if the user exists. If the user doesn't exist, we are going to create a new user and save the user's wallet information to the app_metadata
field.
The key changes are in the new getOrCreateUser
function. The update logic is
For the given wallet, check to see if a user already exists. We use a new table
user_wallet
to lookup a user ID for a wallet address. We will create this table in the next step.If the user doesn't exist, create a new user via the admin auth API. This requires a Supabase client that is created with your project's secret service role key
If the user does exist, fetch the user via their user ID. This requires a Supabase client that is created with your project's secret service role key
Create a Supabase JWT from the user info. This is the same JWT structure returned by other native Supabase login mechanism.
We store the user's wallet information in the app_metadata
field, so we can retrieve it client-side from the the JWT or database-side in RLS policies.
We use the app_metadata
field instead of the user_metadata
because user can update user_metadata
. We do not want users to change their wallet address without verifying ownership, so we use the app_metadata
field which is only modifiable by an admin role.
Create a Table for Mapping User ID to Wallet Address
There is no native Supabase SDK for filtering users based on app_metadata
. To do this, we create a lookup table user_wallet
that maps wallet addresses to user IDs.
From the sidebar menu in the Supabase dashboard, click Table editor
, then New table
.
Enter user_wallet
as the Name
field.
Select Enable Row Level Security (RLS)
.
Create two columns:
user_id
asuuid
with foreign key relationship toauth.user(id)
. Set this as the primary key.wallet_address
astext
Click Save
to create the new table.
Alternative Approaches
We use a lookup table user_wallet
to fetch a user_id
for a given wallet address.
An alternative approach is to query auth.users
directly. At the time of writing this, there is no native Supabase SDK method for query a user based on their app_metadata
. The only way to query the raw_app_metadata
field in auth.users
is to connect and query Postgres directly or create a custom RPC.
Any of these approaches work. Choose the method that best meets your app's requirements.
Trigger Inserts on New User Creation
Now we have lookup a table, but how do we populate it? The recommended approach is to create an on insert trigger in Postgres. This trigger will automatically updates the mapping between user_id
and wallet_address
any time a new user is created in the auth.users
table.
RLS
By leveraging Supabase's native auth.users
table, we can write RLS policies based off user ID rather than the wallet address RLS policy we defined above. This keeps our logic consistent regardless of the user's login mechanism.
However, if we still want to restrict usage based off wallet address, then we can easily do so with the following RLS expression
Build Anything
You now have the foundation to build any application that supports wallet login along with any other Supabase native auth mechanism.
It's important to note that this guide is a template to help you get started. It is not meant to be prescriptive. Use the concepts in this guide and adjust the implementation to meet your applications specific requirements.
If you have any questions, don't hesitate to reach out to team@picketapi.com. We're here to help!
Last updated