By Chirag Patil · · 4 min read

How to Add Supabase OAuth to FastAPI

A comprehensive guide on integrating Supabase OAuth with FastAPI for secure social logins using GitHub, Google, and other providers.

Infrastructurefastapisupabaseoauthauthenticationpythonweb-development

How to add Supabase OAuth in FastAPI

In this post, we'll walk through how to quickly add secure social logins (like Google, GitHub, etc.) to your FastAPI application using Supabase OAuth.

We'll use my pre-built template to get you started in minutes.

Check out the complete code template on GitHub: https://github.com/QueryPlanner/FastAPI-Supabase-Auth-Template.git

The Core Concepts (In a Nutshell)

  • FastAPI: Our high-performance Python web framework for building the API.
  • Supabase: Our Backend-as-a-Service. It will act as a trusted middleman, handling the complex OAuth flow with providers like Google or GitHub so we don't have to.
  • OAuth Flow: The user journey is simple:
    1. User clicks "Login with GitHub" on your app.
    2. Your FastAPI app asks Supabase for a special login URL and sends the user there.
    3. User approves the login on GitHub.
    4. GitHub sends the user back to your FastAPI app with a temporary "code".
    5. Your app exchanges this code with Supabase for a real user session token (JWT).
    6. The user is now logged in!

Step 1: Clone the Template Repository

Let's start by cloning the FastAPI + Supabase template, which contains all the boilerplate code we need.

git clone https://github.com/QueryPlanner/FastAPI-Supabase-Auth-Template.git
cd FastAPI-Supabase-Auth-Template

Next, install the required Python packages.

pip install -r requirements.txt

Step 2: Configure Your Supabase Project

  1. Create a Supabase Project: If you don't have one, go to supabase.com and create a new project.

  2. Enable an OAuth Provider:

    • In your Supabase project, navigate to Authentication -> Providers.
    • Choose a provider you want to enable (e.g., GitHub).
    • You'll see a Redirect URL (also called a Callback URL). Copy this URL. You'll need it in a moment.
    • Now, go to your provider's developer settings (e.g., GitHub's OAuth Apps page) and create a new OAuth App.
    • Paste the Redirect URL you copied from Supabase into the "Authorization callback URL" field on the provider's site.
    • The provider will give you a Client ID and a Client Secret. Copy these back into the matching fields in your Supabase provider settings.
    • Click Save.

Step 3: Configure Your FastAPI Application

The template uses a .env file to manage secret keys. Rename the example.env file to .env and fill it with your Supabase credentials.

# .env

# Find these in your Supabase Project -> Settings -> API
SUPABASE_PROJECT_URL="https://your-project-ref.supabase.co"
SUPABASE_API_KEY="your-public-anon-key"

# Find this in your Supabase Project -> Settings -> API -> JWT Settings
SUPABASE_JWT_SECRET="your-jwt-secret-from-supabase-settings"

Step 4: The Code That Makes It Happen

The magic happens in just two API endpoints in main.py. Let's replace the existing email/password routes with our new OAuth routes.

# main.py

from fastapi import FastAPI, Depends, HTTPException, Request, Query
from fastapi.responses import RedirectResponse
from gotrue.errors import AuthApiError

# Import our custom modules
from supabase_client import supabase
from schemas import AuthResponse
from auth import JWTBearer

app = FastAPI(
    title="FastAPI Supabase OAuth",
    description="A demo of Supabase OAuth with FastAPI.",
    version="1.0.0",
)

# Dependency for our protected routes
auth_dependency = Depends(JWTBearer())

@app.get("/login/{provider}", response_class=RedirectResponse, tags=["Auth"])
async def oauth_login(provider: str):
    """
    Generates the OAuth login URL for the given provider and redirects the user.
    """
    # The redirect_to URL should point back to your backend's callback endpoint.
    # In a real app, this might be a frontend URL.
    url, _ = supabase.auth.sign_in_with_oauth({
        "provider": provider,
        "options": {
            "redirect_to": "http://127.0.0.1:8000/callback"
        }
    })
    return RedirectResponse(url)

@app.get("/callback", response_model=AuthResponse, tags=["Auth"])
async def callback(request: Request, code: str = Query(...)):
    """
    Handles the callback from the OAuth provider.
    Exchanges the authorization code for a user session (JWT).
    """
    try:
        # Exchange the code for a valid session
        session_data = supabase.auth.exchange_code_for_session({"auth_code": code})

        # Return the session details to the user
        return {
            "access_token": session_data.session.access_token,
            "refresh_token": session_data.session.refresh_token,
            "user": {
                "id": session_data.user.id,
                "email": session_data.user.email
            }
        }
    except AuthApiError as e:
        raise HTTPException(
            status_code=400,
            detail=f"Invalid code or failed OAuth exchange: {e.message}"
        )

@app.get("/me", dependencies=[auth_dependency], tags=["Protected"])
async def protected_route(payload: dict = auth_dependency):
    """
    A protected route that only authenticated users can access.
    The user's JWT payload is injected by the JWTBearer dependency.
    """
    user_id = payload.get("sub")
    return {"message": f"Hello from a protected route! Your User ID is: {user_id}"}

How it works:

  1. /login/{provider}: This endpoint simply asks Supabase to create a unique login URL for the provider (like github or google) and immediately redirects the user's browser to that URL.
  2. /callback: This is where the provider sends the user back after they've approved the login. The URL will contain a temporary code. Our code grabs this code and uses supabase.auth.exchange_code_for_session to trade it for a permanent access_token. This token is the key to proving the user is logged in.
  3. /me: This is a protected route. The auth.py file in the template contains a JWTBearer class that acts as a bouncer. It checks for a valid access_token in the request header before allowing access.

Step 5: Run and Test Your OAuth Flow

  1. Start the server:

    uvicorn main:app --reload
    
  2. Initiate Login: Open your web browser and go to the login URL for the provider you configured (e.g., GitHub): http://127.0.0.1:8000/login/github

  3. Authorize: You will be sent to GitHub to authorize the application. Click "Authorize".

  4. Get Your Token: GitHub will redirect you back to http://127.0.0.1:8000/callback, and you'll see a JSON response in your browser containing your access_token. Copy it!

  5. Test the Protected Route:

    • Navigate to the API docs at http://127.0.0.1:8000/docs.
    • Click the Authorize button at the top right.
    • In the popup, type Bearer <your_access_token> and click Authorize.
    • Now, try out the /me endpoint. It will work!

Conclusion

That's it! You've successfully integrated a secure, third-party social login into your FastAPI application. By letting Supabase handle the OAuth complexity, you can focus on building your app's core features while providing a seamless login experience for your users.

Feel free to extend the template and add more providers! Happy coding.

Author: Chirag Patil