supabase-go icon indicating copy to clipboard operation
supabase-go copied to clipboard

Auth issue, anon working

Open YancyFrySr opened this issue 1 year ago • 7 comments

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

YancyFrySr avatar Jun 28 '23 22:06 YancyFrySr

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 avatar Jul 19 '23 15:07 amlwwalker

@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

amlwwalker avatar Jul 19 '23 21:07 amlwwalker

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 avatar Dec 29 '23 11:12 dvcrn

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
}

dvcrn avatar Dec 29 '23 11:12 dvcrn

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)

urjitbhatia avatar Jul 26 '24 07:07 urjitbhatia

@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

saintmalik avatar Aug 07 '24 03:08 saintmalik

@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

urjitbhatia avatar Aug 07 '24 23:08 urjitbhatia