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

Clients should take an optional supabase_token (workaround provided) #658

Closed
turian opened this issue Jan 10, 2024 · 16 comments
Closed

Clients should take an optional supabase_token (workaround provided) #658

turian opened this issue Jan 10, 2024 · 16 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@turian
Copy link

turian commented Jan 10, 2024

Is your feature request related to a problem? Please describe.

I have a serverless application, using just supabase as the backend. (No flask, fastapi, etc.)

I'm writing a CLI client in Python. The only way I can figure out how to auth the user, is to put my access_token into the client using environment variables. (This is because the CLI might run on a remote machine with no browser.) However, getting the supabase client to accept the access_token is difficult, because it uses supabase_key as both the apikey AND bearer token.

Describe the solution you'd like

    def __init__(
        self,
        supabase_url: str,
        supabase_key: str,
        supabase_access_token: Optional[str] = None,
        options: ClientOptions = ClientOptions(),
    ):
        if supabase_access_token is None:
            supabase_access_token = supabase_key

and use supabase_access_token when constructing headers.

Describe alternatives you've considered

    supabase.postgrest.auth(SUPABASE_PRIVATE_TOKEN)

works but is very janky and undocumented.

Additional context
Appears related to #221, #616, #645

This suggests that it is a commonplace issue.

@Atticuszz
Copy link

honestly,supabase_key is identical to access_token,as the first time you call property like postgrest,it will autonomously init by supabase_key

@Atticuszz
Copy link

but it seems like creating client by access token that got after signed in from front-end does not work properly to recognize the User,I mean if u called get_session,will get None,quite wired.it's bug I think

@turian
Copy link
Author

turian commented Jan 13, 2024

@Atticuszz Actually there's a difference between the site's private supabase key and the user's access token.

If you do the workaround that I proposed, then a CLI client can access the user's data. If you use the user's access token as the supabase key, you will get an authentication error from supabase.

@Atticuszz
Copy link

@turian thanks for point out it!i do test access token as key,auth failed ,cause the auth_token set None if we do not sign in with posswords etc,my solution is use the original key in supabase project,and use the clien.auth.set_session(access_token,refresh_token),the client will be treated as the owner of acces_token
repo

@silentworks
Copy link
Contributor

I will investigate this a bit more to see if this is been handled differently with the supabase-js library.

@silentworks silentworks self-assigned this Jan 14, 2024
@silentworks silentworks added the bug Something isn't working label Jan 14, 2024
@silentworks silentworks added this to the Stable milestone Jan 14, 2024
@Atticuszz
Copy link

Atticuszz commented Jan 14, 2024

i try to fix it in PR #656

@silentworks
Copy link
Contributor

silentworks commented Feb 29, 2024

You can pass a user's access token using a header

create_client(
        url,
        key,
        options=ClientOptions(
            headers={"Authorization": f"Bearer {access_token}"},
        ),
    )

@Atticuszz
Copy link

You can pass a user's access token using a header

create_client(
        url,
        key,
        options=ClientOptions(
            headers={"Authorization": f"Bearer {access_token}"},
        ),
    )

thanks(′▽`ʃ♡ƪ) i got it

@sebasortiz-dev
Copy link

sebasortiz-dev commented Mar 13, 2024

@silentworks It does not work, in the init.

The create_client, eventually tries to create a SyncClient and the init has this code

        self.supabase_key = supabase_key
        self._auth_token = {
            "Authorization": f"Bearer {supabase_key}",
        }
        options.headers.update(self._get_auth_headers())

It replaces whatever option headers you passed in the Authorization header, when creating the client.

As the OP mentioned the workarround of using client.postgrest.auth(access_token) works, because it rewrites the header auth after the initialization.

@sebasortiz-dev
Copy link

@Atticuszz I saw you fast api supabase boilterplate ( i'm working with fast api too) and I think you need to support the custom auth. In my use case the fastapi relies on the RLS of supabase + supabase auth, in order to RLS to work correctly you need to pass the user's access_tokens to the supabase postgrest client.

@Atticuszz
Copy link

Atticuszz commented Mar 13, 2024

@Atticuszz I saw you fast api supabase boilterplate ( i'm working with fast api too) and I think you need to support the custom auth. In my use case the fastapi relies on the RLS of supabase + supabase auth, in order to RLS to work correctly you need to pass the user's access_tokens to the supabase postgrest client.

Thanks for your suggestion .my solution is letting supabase js take care the auth,case i think it's easier to work with ui with it,and what's custom auth?my boilterplate is auto pass acces stoken to supabase and pos client by deps

@sebasortiz-dev
Copy link

My workarround, to get this done, rewrite the _auth_token. With both headers.

def create_supabase_client(access_token: str) -> Client:
    supabase: Client = create_client(
        settings.SUPABASE_URL,
        settings.SUPABASE_API_KEY,
    )
    # This is the hacky way to get supabase doing what we need using users JWT
    supabase._auth_token = {
        "apiKey": f"{settings.SUPABASE_API_KEY}",
        "Authorization": f"Bearer {access_token}",
    }
    return supabase

@huyouare
Copy link

My workarround, to get this done, rewrite the _auth_token. With both headers.

Thanks @sebasortiz-dev! I was struggling with a similar issue (using RLS but with 3rd party auth). The previous suggestions did not work for me, but yours did.

@hssumar
Copy link

hssumar commented Apr 13, 2024

My workarround, to get this done, rewrite the _auth_token. With both headers.

def create_supabase_client(access_token: str) -> Client:
    supabase: Client = create_client(
        settings.SUPABASE_URL,
        settings.SUPABASE_API_KEY,
    )
    # This is the hacky way to get supabase doing what we need using users JWT
    supabase._auth_token = {
        "apiKey": f"{settings.SUPABASE_API_KEY}",
        "Authorization": f"Bearer {access_token}",
    }
    return supabase

This is not enough, the headers are being duplicated all over the place in the client:

The options object:
https://github.com/supabase-community/supabase-py/blob/main/supabase/_sync/client.py#L70

The auth client:
https://github.com/supabase-community/supabase-py/blob/main/supabase/_sync/client.py#L80

The following hack works for me, where we dynamically subclass the object to patch _get_auth_headers method:


my_auth_header = "Bearer ..."
class MyClient(supabase.Client):

    def _get_auth_headers(self) -> Dict[str, str]:
        """Fix the helper function"""
        return {
            "apiKey": self.supabase_key,
            "Authorization": my_auth_header
        }

my_client = MyClient.create(url, key)
my_client._auth_token["Authorization"] = my_auth_header

@silentworks
Copy link
Contributor

This PR should have resolved this issue #766.

@silentworks
Copy link
Contributor

Closing this out as I believe it has been resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Done
Development

No branches or pull requests

6 participants