Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supabase Storage API Session is None in FastAPI with AuthBearer #869

Open
elblogbruno opened this issue Jul 23, 2024 · 7 comments
Open

Supabase Storage API Session is None in FastAPI with AuthBearer #869

elblogbruno opened this issue Jul 23, 2024 · 7 comments

Comments

@elblogbruno
Copy link

Bug report

Describe the bug

I am using supabase-py with fastapi.

I made a login endpoint that uses sign_in_with_password():

dic = {"email": email, "password": password}
            res = supa.auth.sign_in_with_password(dic)

It returns access_token and refresh_token that then I save on my client. My client does requests with the access_token and I use an
AuthBearer on fastapi to validate the token and get current user on every endpoint I have:

class AuthBearer(HTTPBearer):
    def __init__(self, auto_error: bool = True):
        super().__init__(auto_error=auto_error)

    async def __call__(
        self,
        request: Request,
    ):
        credentials: Optional[HTTPAuthorizationCredentials] = await super().__call__(
            request
        )
        self.check_scheme(credentials)
        token = credentials.credentials  # pyright: ignore reportPrivateUsage=none
        return await self.authenticate(
            token,
        )

    def check_scheme(self, credentials):
        if credentials and credentials.scheme != "Bearer":
            raise HTTPException(status_code=401, detail="Token must be Bearer")
        elif not credentials:
            raise HTTPException(
                status_code=403, detail="Authentication credentials missing"
            )

    async def authenticate(
        self,
        token: str,
    ) -> User:
        if verify_token(token):
            # supa.postgrest.auth(token=token) 
            return supa.auth.get_user(jwt=token).user
        else:
            raise HTTPException(status_code=401, detail="Invalid token or api key.")

 

def get_current_user(user: User = Depends(AuthBearer())) -> User:
    return user

When using storage api and being logged in, session is None so consequent storage requests give errors.

@router.get('/uploads/{sheet_id}/{file_name}')
async def get_uploads(request:Request, sheet_id: str, file_name: str, user = Depends(get_current_user)):
    user_id = str(user.id) 
    key = os.path.join(user_id, sheet_id, file_name) 

    print(f"Getting object {key}")  
    print(supa.auth.get_session()) # prints none
    print(supa.storage.from_("test").list(os.path.join(user_id, sheet_id))) # returns []
    image = supa.storage.from_("test").create_signed_url(key, expires_in=3600) # returns error file not found

There are files in there.

I made a sample file to test:

from supabase import Client 

import dotenv

dotenv.load_dotenv()

client = Client(os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_KEY"))
dic = {"email": os.getenv("SUPABASE_TEST_EMAIL"), "password": os.getenv("SUPABASE_TEST_PASSWORD")}
res = client.auth.sign_in_with_password(dic)
access_token = res.session.access_token
user = client.auth.get_user()

# 5f9dc916-0a06-4c09-a4f0-3c4cf16ca7f2 is user id.

print(client.storage.from_("test").list("5f9dc916-0a06-4c09-a4f0-3c4cf16ca7f2/28c90514-a9c3-4a33-8af1-5dbcca40d04a"))
print(client.storage.from_("test").create_signed_url("5f9dc916-0a06-4c09-a4f0-3c4cf16ca7f2/28c90514-a9c3-4a33-8af1-5dbcca40d04a/thumbnail.jpg", expires_in=86400)) # , options

and this gives correct info!

Maybe I am doing something wrong?

Many thanks
Bruno

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Set up FastAPI with supabase-py.
  2. Implement login endpoint and AuthBearer as shown above.
  3. Attempt to access the storage API with an authenticated session.
  4. Observe that supa.auth.get_session() returns None, and storage operations fail.

Expected behavior

supabase-py should maintain the session after authentication, allowing storage API calls to succeed as they do in the standalone script.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: [e.g. macOS, Windows]
  • Browser (if applies) [e.g. chrome, safari]
  • Version of supabase-js: [e.g. 6.0.2]
  • Version of Node.js: [e.g. 10.10.0]

Additional context

Add any other context about the problem here.

@elblogbruno elblogbruno added the bug Something isn't working label Jul 23, 2024
@silentworks silentworks removed the bug Something isn't working label Jul 23, 2024
@silentworks
Copy link
Contributor

This is likely more related to how FastAPI handles requests than the library itself. You should use some sort of persistent storage setup with FastAPI as sessions aren't shared between requests.

@elblogbruno
Copy link
Author

Got it thanks!
I will look into the safest way of doing it!

@Mamdouh66
Copy link

I was having a kinda of a similar issue where I only send the access_token to the backend and I would just have RLS problems when communicating with the supabase db.

Because the only way to set the session in the backend is by using the set_session function where you have to pass both the access_token and refresh_token which didn't make sense for my to pass the refresh token from my frontend to my backend, and I was kinda stuck because the get_session function doesn't take any params and we just can't get it.

after lot's of searching a solution that helped my was the following

      client = await create_async_client(
          settings.SUPABASE_PROJECT_URL,
          settings.SUPABASE_API_KEY,
          options=ClientOptions(headers={"Authorization": f"Bearer {access_token}"}),
          )

which is basically when creating the client you have to pass the access_token like the above example, it would help with communicating with the supabase api's. But still even with that the get_session still has the same problem.

idk if that helps

@elblogbruno
Copy link
Author

elblogbruno commented Sep 12, 2024

Hi, Many thanks for answering.
So you create a new client for every endpoint on every request if I understand correctly?
Finally I manage myself to append access token directly to storage object in python to get RLS and authentication working with storage module:

supa: Client = create_client(url, key)

supa.storage.session.headers["Authorization"] = f"Bearer {access_token}" 

image = supa.storage.from_("test").create_signed_url(key, expires_in=3600)

Thanks for your answer
Bruno

@lukajose
Copy link

lukajose commented Dec 7, 2024

This is not just a FastAPI issue is a supabase issue. I am sending an access token from my client to my server and setting the client as you would expect:

options = ClientOptions(
                    headers={
                        "Authorization": f"Bearer {auth_token}",
                        "Content-Type": "application/json"
                    },
                )
supabase_user = create_client(
                os.environ.get('SUPABASE_URL', ''),
                os.environ.get('SUPABASE_ANON_KEY', ''),
                options=options
            )
user = supabase_user.auth.get_user()
print(f"This doesnt work, User: {user}")` // This is None

user = supabase_user.auth.get_user(auth_token)
print(f"This works, User: {user}")

All RLS policies work as expected.

@silentworks
Copy link
Contributor

@lukajose This is the correct behavior. Setting the Authorization header doesn't set a session, it only passes that Authorization header to the API per request. supabase.auth.get_user() on it's own doesn't use the Authorization header that is set, it uses the session data or the auth_token you set when calling .get_user. There are huge misconceptions around this and since it's not well documented it throws users off. I was just thinking of doing a blog post or a video about this last week.

@lukajose
Copy link

lukajose commented Dec 7, 2024

Yeah that would be helpful, the current client is a bit confusing. I would expect the client to just return the user without passing the token. How would you set the session then?.

@lukajose This is the correct behavior. Setting the Authorization header doesn't set a session, it only passes that Authorization header to the API per request. supabase.auth.get_user() on it's own doesn't use the Authorization header that is set, it uses the session data or the auth_token you set when calling .get_user. There are huge misconceptions around this and since it's not well documented it throws users off. I was just thinking of doing a blog post or a video about this last week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants