Logo

dev-resources.site

for different kinds of informations.

Validating a Supabase JWT locally with Python and FastAPI

Published at
3/15/2024
Categories
supabase
jwt
python
fastapi
Author
zwx00
Categories
4 categories in total
supabase
open
jwt
open
python
open
fastapi
open
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"'},
        )

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

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.

Featured ones: