supabase / auth-py

Python client implementation for Supabase Auth
MIT License
68 stars 37 forks source link

Can't Retrieve/Set a User/Session: "Session from session_id claim in JWT does not exist" #503

Open Slumberdac opened 3 months ago

Slumberdac commented 3 months ago

With the recent update to supabase's auth system, trying to retrieve a user throws a 403 rather than the 401 it used to send if they have no session_id in auth.session table (for more information).

This makes it virtually impossible to retrieve a user created via the API (in my experience) because they may not have a row in the session table.

Thus no authentication can occur using access tokens, instead raising this exception: gotrue.errors.AuthApiError: Session from session_id claim in JWT does not exist

For example, this code which uses the access token stored in the user's cookies to get the current user for later, no longer works, the previously mentioned AuthApiError happens and current_user is never set.

current_user = None
try:
     access_token = request.cookies.get("access_token")
     current_user = settings.supabase.auth.get_user(access_token)
          current_user = current_user.user if current_user else None
except gotrue_exceptions.AuthError as exc:
     print(exc)
...

This issue is making it near impossible for my project to work and a simple return to a prior version doesn't work because its root is the previously mentioned update from supabase

J0 commented 3 months ago

This makes it virtually impossible to retrieve a user created via the API (in my experience) because they may not have a row in the session table.

Hey,

Auth team here - thanks for the query! Do you mind expanding a little more on how the user is created? (e.g. create user via admin api, signup) Generally it should be possible to obtain a session from Supabase auth by signing in after the user is created. Let me know if there are any potential concerns that I've missed though

Lmk! Joel

Slumberdac commented 3 months ago

Hi here's a code snippet of the creation of users in my project:

auth_router = APIRouter(prefix="/auth")

@auth_router.patch(
    "/signup",
)
async def signup_user(
    credentials: Credentials,
    auth_response: Response,
    response: ResponseSuccess = Depends(ResponseSuccess.depends),
):
    """
    The api/v1/signup endpoint
    Creates a new user, authenticates him and
    creates the tokens in cookies
    """
    email = credentials.email
    phone = credentials.options.get("data", {}).get("phone")

    # Retrieval of session to instantiate the authentication cookies
    session = settings.supabase.auth.sign_up(credentials.model_dump()).session

     # Avoid accidentally authenticating the API and not the client
    settings.supabase.auth.sign_out()

    auth_response.set_cookie(
        key="access_token", value=session.access_token, httponly=True
    )
    auth_response.set_cookie(
        key="refresh_token", value=session.refresh_token, httponly=True
    )
    ...

This adds users to the auth.users table (see here with mock users) image

unless I'm misunderstanding, going through supabase.auth.sign_up() does in fact act as a sign in once the user is created which is why I'm perplexed when the sessions table looks like this where the only sessions are for users created through the dashboard: image

I might also add that trying to sign in afterwards doesn't resolve the issue as even running this endpoint does not fix it:

@auth_router.patch(
    "/signin",
)
async def signin_user(
    credentials: Credentials,
    auth_response: Response,
    response: ResponseSuccess = Depends(ResponseSuccess.depends),
    access_token: str | None = Cookie(None),
):
    """
    The api/v1/signin endpoint
    Authenticates the user and
    creates the tokens in cookies
    """

    # collect the session with the correct credentials
    session = settings.supabase.auth.sign_in_with_password(
        credentials.model_dump(exclude_unset=True)
    ).session

    # Avoid the API being authenticated
    settings.supabase.auth.sign_out()

    # Set cookies
    auth_response.set_cookie(
        key="access_token", value=session.access_token, httponly=True
    )
    auth_response.set_cookie(
        key="refresh_token", value=session.refresh_token, httponly=True
    )

    response.data = {"uuid": session.user.id}
    return response.model_dump(exclude_none=True)

Finally I forgot to include this, the traceback from trying to run the previously mentionned settings.supabase.auth.get_user(access_token) code:

Traceback (most recent call last):
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\gotrue\_sync\gotrue_base_api.py", line 113, in _request
    response.raise_for_status()
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\httpx\_models.py", line 749, in raise_for_status
    raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '403 Forbidden' for url '%PROJECTURL%/auth/v1/user'
For more information check: https://httpstatuses.com/403

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\starlette\routing.py", line 72, in app
response = await func(request)
           ^^^^^^^^^^^^^^^^^^^
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\fastapi\routing.py", line 278, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\fastapi\routing.py", line 191, in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "project\app\api\auth.py", line 100, in signin_user
    raise exc
  File "project\app\api\auth.py", line 93, in signin_user
    user = settings.supabase.auth.get_user(access_token).user
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\gotrue\_sync\gotrue_client.py", line 396, in get_user
    return self._request("GET", "user", jwt=jwt, xform=parse_user_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "%LOCALAPPDATA%\Programs\Python\Python311\Lib\site-packages\gotrue\_sync\gotrue_base_api.py", line 118, in _request
    raise handle_exception(e)
gotrue.errors.AuthApiError: Session from session_id claim in JWT does not exist

I hope this can help, I've seen it was added to the supabase python board so if there's any other questions regarding this please let me know I'll do whatever I can to help out

silentworks commented 2 months ago

@Slumberdac I'm a little confused by your code examples above as in one of them I can see you signing in and then immediately after you sign out. Can you provide an complete reproducible repository with the issue you are facing as the small abstract of code your provided isn't that helpful?

Slumberdac commented 1 month ago

sorry for delay I'll try and create a basic repository as an example and come back to you

Slumberdac commented 1 month ago

Sorry again for the delay but hopefully this example helps understanding where i might have gone wrong. Just like my original issue it is ran with fastapi. Everything should be there by following the README please inform me if anything is wrong.

https://github.com/Slumberdac/Example

Thank you!