⚑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

Setup Picket

First, we'll create a new project in our Picket dashboard.

1. Go to your Picket Dashboard

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.

Example Project w/ Publishable and Secret Key Redacted

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.

Create a new Supabase 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 as text

  • wallet_address as text

  • completed as bool with the default value false

  • created_at as timestamptz with a default value of now()

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.

Restrict Access to Todos to the Associated Wallet Address

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 step

  • PICKET_PROJECT_SECRET_KEY => Copy the secret key from the Picket project you created in the previous step

  • NEXT_PUBLIC_SUPABASE_URL => You can find this URL under "Settings > API" in your Supabase project

  • NEXT_PUBLIC_SUPABASE_ANON_KEY => You can find this project API key under "Settings > API" in your Supabase project

  • SUAPBASE_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

  1. Verifies server-side that the user is authenticated and if they are not redirects them to the homepage

  2. Checks to see if they already have todos . If so, it returns them. If not, it initializes them for the users

  3. We 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 [email protected]

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

  1. 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.

  2. 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

  3. 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

  4. 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.

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 as uuid with foreign key relationship to auth.user(id). Set this as the primary key.

  • wallet_address as text

Click Save to create the new table.

Create a lookup table for user_id to wallet_address

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 [email protected]. We're here to help!

Last updated