I'm playing around with Supabase, but I'm struggli...
# help
j
I'm playing around with Supabase, but I'm struggling with the RLS policies. I've created a simple table, and when RLS is not applied, I can POST and GET using Postman with just the ANON key (as apikey + bearer token). I am able to insert and select rows. However, the purpose of the RLS is for any user to only be able to interact with his own data, right? I've created a user, which then has an
uid
, but how do I incorporate that uid in the POST/GET requests? Is it some header?
g
Sort of unclear if you are testing , or want to not use the supabase client with auth/signin, etc. You can set RLS to read headers (postgrest.org shows code for reading http headers) which can be used in RLS. Otherwise you have to pass in a token. Like discussed here: https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwts I've seen several discussions here and on github around these topics, but no all in one demo.
j
Hi, and thanks for replying 🙂 Yes, I'm just testing Supabase out. I realize for RLS to work, I need the token for the relevant user -- which would normally be gotten by that user actually logging in. I'll check out the link!
What I did so far is to just "Invite" myself as a user, but upon clicking the link in the email I'm just redirected to localhost:3000, so no password etc is ever set...
Point being, I can register/invite myself (my email) as a user (and get an uid) but I can't set a password => can't log in => no token
g
In my debugging set up I have signin with default user and password. One time I change it to signup, get email, confirm, then change code to signin forever (unless I delete the user).
j
I have to admit I didn't quite follow that 😶 What does "default user/pass" mean, how does that work? Am I correct in that my problem (part of it, at least) is that I need to actually register a complete user, in order to be able to log in and get a token?
g
var supabase = supabase.createClient(SUPABASE_URL, SUPABASE_KEY,{realtime:{logger:logger}}) async function app () { const { user, error } = await supabase.auth.signIn({ email: 'xxx@yyy.com', password: 'password', })
j
Just Invite (email link to localhost) is not enough
g
In that I just change signin to signup for the very first time. That uses my real email and any password.
j
My potential use case is to have some embedded sensors (ESP8266) configured to POST sensor data for/as a specific user, and then have a web app (frontend) where the corresponding human user would log in, and see his data
So I envision the user registering for "my service", and getting some token to then configure into the sensors (API_KEY), such that the sensors would be able to POST "on his behalf"
g
So then you ARE trying to not need auth client and still have RLS in a real app versus just testing. In that case you have to look at using a header with id code which is fine if super security is not needed or research minting tokens to imbed the id and pass the tokens along the lines in the first link I provided. Beyond that I personally can't get you further, other than there are discussion around here and github for these types of things.
j
🙂 thanks a lot! So, just so I understand:
You're saying I don't want an auth client. Is that because the sensors couldn't be able to "log in"?
I'd sort of would require some "persistent log in", wouldn't I, for the sensors. Some secret/token to embed into the firmware. If this is the same as what you just said, then maybe I understood it 😉 I'll check out the link! Thanks again!
g
I was in one or two github discussions on this exact thing with sensors. I personally would avoid auth code and maintaining logins for stand alone devices, unless you need high security. You can even combine a hard coded device id from the device that the user inputs into their account on supabase and use that in RLS with or without a code they put in the device. The risk is if they never change it is possible for someone to find a device id and generate fake inserts and put data in that one device data in supabase. I'll do a quick search.
j
exactly that: fake inserts. I was thinking maybe one could configure the database "rules" to simply allow for "anonymous _INSERT_" (POST), based on the device id (and why not also some uid, which the user can configure into the device during setup). But it would then be very easy for someone to fake data (or even spam with viagra ads etc), which would render the system quite useless
Not sure how easy it would be, though, to "guess" such a 10-digit device ID... The sensors would be POST-ing the sensor data, so everything could be set up to use SSL, right (encrypted POST body)?
But yeah, I'll let you off the hook 😄
g
Well you can make the device not guessable by being long, or some mac address so spamming is not issue, if user has access to device then you can even have them set a passcode to add to it. No one can read the data but the user from the data base, so it is only someone trying to fake one device if they found the key. Also jwt's can be faked too, just their life is an hour or what ever you set it to. In that span anyone who has it can write to the database.
https://github.com/supabase/supabase/discussions/3805 This was one, but it is mainly filled in with my silly ideas. I can't find the other one I saw.
j
Yes, password configured into the sensor should pretty much solve the problem (if HTTPS). The user would still have to configure his uid (or something) into the device, and this would be added onto the POST requests (header?) s.t. the requests would pass/satisfy the database rule (RTS)
g
You use the code here to read headers and compare to table row device_code for RLS: https://postgrest.org/en/stable/api.html#http-context
j
That's so cool that you linked to that last one. That guy, Meier Thomas, is apparently doing something quite similar (as you said) and I found some questions from him in some other forum. I tried to find some more info on him/his project, but couldn't find this Github page you're now linking to 😄
It really is fun how identical his issue is, to mine 😄
Quite basic issue, though. Perhaps the Supabase docs might include some section/example on this...
g
You will need a separate device id either provided to user to program or from the device to have all data stored with that as id column. Your password/key would be in another table paired with the device id and you RLS that paired table, but only use the device Id to key the real data. Otherwise you have to change id on all data rows when you change password.
j
I think I get that part -- I just still can not find any (to me) understandable docs on how to make a query (POST or GET) which would have no token but rather some header which would pass the RLS. If the RLS rule is simply
USING (auth.uid() = user_id)
then "auth" is coming from the request, right?
g
Your RLS could use a custom function just like auth.uid()
Copy code
create or replace function getDevId() returns varchar as $$
  select nullif(current_setting('request.headers', true)::json->>'super_secret_device_key', '')::varchar;
$$ language sql stable;
You set the 'super_secret_device_key' header in your post/get call
j
I see! But in what header key (field) then?
I mean, the headers are key/value, aren't they?
g
In javascript fetch: headers: { 'super_secret_device_kdy': 'key'
j
so that could be the key, I see
key, as in header field name
and the content there is the key bytes. But what table/column would that key payload then be checked against?
g
The function I showed (if I wrote it write, copying from supabase auth.uid() ) will return the key string from that header.
j
ah, so you'd use it something like this,
USING (getDevId() = user_key)
getDevId just gets the key payload
g
user_key would come from either a paired tabled with the fixed id (which would just be part of your insert columns
Yes getDevId gets the key from the device (should really have called it a key or password.
j
Maybe something along these lines, then
USING (EXISTS (SELECT * FROM t1 INNER JOIN t0 ON (t1.t0id = t0.id) WHERE t0.u = session_user AND t1id = t1.id))
I mean, joining the paired table to get the actual SSDK (hehe) given the
user_id
g
I think at this point you should play around a bit, and ask a new if you run into specific issues. So much will depend on how you set ids, and map user to one or more devices, etc. All the header is really doing is isolating the key from your actual table data you insert (which should contain a fixed id per device).
j
yes! I think I have a quite decent overview 🙂 Thanks for the help!!