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

Auth issue, anon working #19

Open
YancyFrySr opened this issue Jun 28, 2023 · 7 comments
Open

Auth issue, anon working #19

YancyFrySr opened this issue Jun 28, 2023 · 7 comments

Comments

@YancyFrySr
Copy link

YancyFrySr commented Jun 28, 2023

Hi all, I may be doing something stupid here so please forgive me.

When i run a sign in, how do i then use that logged in user to perform requests against the DB?

My query is working with RLS allowing anon however when RLS is enabled to only allow authenticated users I don't get a response.

I've tried multiple ways of enabling auth only for SELECT on the table. for example:

CREATE POLICY "Enable read accesas for authenticated users" ON "public"."TESTTABLE"
AS PERMISSIVE FOR SELECT
TO authenticated
USING (true)

My call to signIn does also respond with a user token.

In supabase logs it appears that no auth header is added to the requests:

"headers": [
          {
            "accept": "application/json",
            "cf_cache_status": null,
            "cf_connecting_ip": "xxxx",
            "cf_ipcountry": "xx",
            "cf_ray": "xxx",
            "content_length": "4",
            "content_location": null,
            "content_range": null,
            "content_type": "application/json",
            "date": null,
            "host": "xxxx.supabase.co",
            "prefer": null,
            "range": null,
            "referer": null,
            "sb_gateway_version": null,
            "user_agent": "Go-http-client/2.0",
            "x_client_info": null,
            "x_forwarded_proto": "https",
            "x_forwarded_user_agent": null,
            "x_kong_proxy_latency": null,
            "x_kong_upstream_latency": null,
            "x_real_ip": "xxx"
          }

Any help would be appreciated

@amlwwalker
Copy link

Would really appreciate this as well. I have printed out the RESTful request statement in the ExecuteWithContext function in postgrest-go/request_builder.go with

command, _ := http2curl.GetCurlCommand(req)
and I can see both the authorization and apikey headers are there. So I am not sure what we need to do to allow access via RLS.
Do we need to attach a session (user.User.Session) potentially somehow? @nedpals - would really appreciate some help if you can!
Thanks

@amlwwalker
Copy link

@YancyFrySr
I solved it.

once you have logged in and have a user's access_token, you can do

	cli.DB.AddHeader("Authorization", "Bearer " + user.AccessToken)
	if err := cli.DB.From("profiles").Select("*").Eq("user_id", user.User.ID).Execute(&s); err != nil {
		panic(err)
	} else {
		fmt.Println(s)
	}

and then it will add that as the header

@dvcrn
Copy link
Contributor

dvcrn commented Dec 29, 2023

Is this the official solution? I was also under the impression that we should be able to pass auth information through context, or create a session-ed client somehow.

This is a bit problematic because for example the Storage pkg does not have a AddHeader method, and I'm suspecting the problem I'm facing right now is because of that. File uploads for anon work, but not for authenticated, likely because the Storage stuff doesn't add auth info

@dvcrn
Copy link
Contributor

dvcrn commented Dec 29, 2023

Okay got it working. Reading through the code, there's a comment above apiKey within the client that it can be a client key as well:

type Client struct {
	BaseURL string
	// apiKey can be a client API key or a service key
	apiKey     string
	HTTPClient *http.Client
	Admin      *Admin
	Auth       *Auth
	Storage    *Storage
	DB         *postgrest.Client
}

So we have to create a user scoped client that uses client API key:

	userScopedClient := supa.CreateClient(client.BaseURL, user.AccessToken, true)

/EDIT: okay doing that is giving me "invalid api_key" errors when using DB actions. I ran out of time for debugging so just created 2 clients, one for upload and one for db..

func NewSupabase(client *supa.Client, user *supa.AuthenticatedDetails) *Supabase {
	userScopedClient := supa.CreateClient(client.BaseURL, user.AccessToken, true)
	client.DB.AddHeader("Authorization", "Bearer "+user.AccessToken)

	s := Supabase{
		user:           user,
		userScopedSupa: userScopedClient,
		normalSupa:     client,
	}

	return &s
}

@urjitbhatia
Copy link

So we have to create a user scoped client that uses client API key:

	userScopedClient := supa.CreateClient(client.BaseURL, user.AccessToken, true)

/EDIT: okay doing that is giving me "invalid api_key" errors when using DB actions. I ran out of time for debugging so just created 2 clients, one for upload and one for db..

func NewSupabase(client *supa.Client, user *supa.AuthenticatedDetails) *Supabase {
	userScopedClient := supa.CreateClient(client.BaseURL, user.AccessToken, true)
	client.DB.AddHeader("Authorization", "Bearer "+user.AccessToken)

	s := Supabase{
		user:           user,
		userScopedSupa: userScopedClient,
		normalSupa:     client,
	}

	return &s
}

Also wasted a ton of time on this - apparently it works on the local supabase docker stack but for production we have to set the apiKey separately.

In case someone else runs into this, apparently what's missing is the apiKey header - which only gets set if debug is true while creating the client. That too, sets the wrong key, since it is setting the access key (possibly user scoped as in this case).

Explicitly adding the anon key using like this works:

userScopedDB :=  supa.CreateClient(URL, userAccessToken, false).DB
userScopedDB.AddHeader("apikey", anonKey)

@saintmalik
Copy link

@urjitbhatia would you mind sharing the full snippet of what worked for you, spent close to 8hrs debugging before finally seeing this issues open here too, when you declared the new client how did you use it to query the db

@urjitbhatia
Copy link

urjitbhatia commented Aug 7, 2024

@saintmalik sure, happy to help.

With either of the following approaches, you will get a user scoped DB connection which will honour the RLS policies of your supabase tables.

Situation 1

If you are using the postgrest client:

Using debug = false

// create a db instance as usual
debug := false
userScopedDB :=  supa.CreateClient(URL, userAccessToken, debug).DB

// if debug is false (which it should be, for production), you have to add this next header explicitly
userScopedDB.AddHeader("apikey", anonSupaKey)

This makes it work in production. The problem is that it works without that extra explicitly set header locally so it's hard to debug.

Using debug = true

debug := true
userScopedDB :=  supa.CreateClient(URL, userAccessToken, debug).DB

This works locally as well as in production but I am not sure if it's the best approach setting debug to true in production.

Situation 2

If you are using the native postgres sql framework:

sess, err := pgx.Connect(context.Background(), dsn)
// ... handle err
const restrictQueriesFmt = `set session role authenticated; SET request.jwt.claims to '{"sub":"%s"}';`
_, err := sess.Exec(context.Background(), fmt.Sprintf(restrictQueriesFmt, userId))

You get the userId from the JWT token...

FWIW, I ended up using the native pgx client rather than the postgrest client since it has better sql support and opens up the ability to use ORMs etc

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

5 participants