supabase-go
supabase-go copied to clipboard
Auth issue, anon working
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
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
@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
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
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
}
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 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 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