FastAPI with Supabase Auth

Updated: May 2024 Published: Aug 2023 2 min read

TLDR the trick is to send the access token that is received from the Supabase session on frontend as headers in the request to the FastAPI and then create middleware to validate that token with Supabase.

This blog details using supabase-py on a FastAPI layer in between frontend using Supabase Auth and Supabase itself.

SolutionLink to the Solution section of this page

Create middleware that handles the authentication with Supabase. This will allow the FastAPI endpoints to make authenciated requests to Supabase using the Supabase client library without any extra work. The middleware can then add the user_id of the requestor into the state of the request so that each endpoint can access the user_id.

CodeLink to the Code section of this page

FrontendLink to the Frontend section of this page

import { supabase } from "./supabaseClient";
const session = await supabase.auth.getSession();
const tokens = session?.data?.session?.access_token;
const response = await fetch(apiUrl as RequestInfo, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${tokens}`,
},
body: JSON.stringify(body),
});

FastAPI Supabase Client SetupLink to the FastAPI Supabase Client Setup section of this page

from supabase import create_client, Client
from fastapi import FastAPI, Request
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_ANON_KEY = os.getenv("SUPABASE_ANON_KEY")
supabase: Client = create_client(SUPABASE_URL, SUPABASE_ANON_KEY)
app = FastAPI()

FastAPI MiddlewareLink to the FastAPI Middleware section of this page

@app.middleware("http")
async def add_authentication(request: Request, call_next):
if request.method == "OPTIONS":
return await call_next(request)
token = request.headers.get("authorization", "").replace("Bearer ", "")
if not token:
return Response("Unauthorized", status_code=401)
try:
auth = supabase.auth.get_user(token)
request.state.user_id = auth.user.id
supabase.postgrest.auth(token)
except Exception:
return Response("Invalid user token", status_code=401)
return await call_next(request)

In the endpoint functions the user_id can be accessed like this:
FastAPI:
user_id = request.state.user_id
Strawberry GraphQL on FastAPI:
user_id = info.context["request"].state.user_id

Example RLS policyLink to the Example RLS policy section of this page

  • Operation: INSERT
  • Target Roles: authenticated
  • WITH CHECK Expression: (auth.uid() = user_id)

If this is the only policy applied to the table for INSERT only authenticated users will be able to create entries, and those entries’ user_id column will need to match their own user_id that is stored automatically in the Auth Users table by supabase when they login.

ResourcesLink to the Resources section of this page