DEV Community

zwx00
zwx00

Posted on • Edited on

Validating a Supabase JWT locally with Python and FastAPI

Supabase is nice, but the functionality it provides for verifying tokens (auth.get_user) for some inexplicable reasons makes a round trip to their end, and this trip in fact takes quite a while (up to 600ms).

JWT tokens should by their very nature be verifiable locally, provided we know the secret. Luckily Supabase does expose this JWT secret under project settings.

Here's how a FastAPI dependency to get user email and verify their authenticity using the token alone might look like:

import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

from katalystai.conf import SETTINGS



async def get_user_email(
    res: Response,
    cred: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=False)),
):

    if cred is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Bearer authentication required",
            headers={"WWW-Authenticate": 'Bearer realm="auth_required"'},
        )

    try:
        jwt_result = jwt.decode(
            cred.credentials,
            SETTINGS.supabase_jwt_secret,
            audience="authenticated",
            algorithms=["HS256"],
        )

        return jwt_result["email"]
    except jwt.exceptions.PyJWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": 'Bearer realm="auth_required"'},
        )
Enter fullscreen mode Exit fullscreen mode

We can then use this "middleware" like so:

from fastapi import Depends
from katalystai.auth import get_user_email

@router.post
async def create_thing(user_email: str = Depends(get_user_email)):
    pass
Enter fullscreen mode Exit fullscreen mode

Supabase python client overall is not that nice, it's kinda slow and unreliable. I also suggest switching to another ORM and communicating with Postgres directly instead of using their community python client. I had luck using tortoise, which has excellent async support and clean Django-like API.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs