Closed DukeManh closed 2 years ago
This doc is really useful for understanding how Supabase uses JWTs:
https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwts
Specifically:
In Supabase we issue JWTs for three different purposes:
- anon key: This key is used to bypass the Supabase API gateway and can be used in your client-side code.
- service role key: This key has super admin rights and can bypass your Row Level Security. Do not put it in your client-side code. Keep it private.
- user specific jwts: These are tokens we issue to users who log into your project/service/website. It's the modern equivalent of a session token, and can be used by a user to access content or permissions specific to them.
The anon key
is OK to put in browser JS (e.g., it's not a secret). You send it with API requests when you talk to the database. It's signed, and you can't get passed Kong (the API gateway) without it. It depends on there being row-level security on the database. Supabase restricts what can be done on a given row of data in the db at the postgres level (e.g., you can say that the anon key can read from some some table, but not write). So we need to add this when we create our tables.
The service role key
is only for use on the server. You can't expose this one, as it gives admin rights. It's useful when you need to do things in the database from a backend serivce.
The user access token
is the one you get when you use supabase.auth.signIn()
. It's an identity token for the user, and you'd pass that along with an apikey
to make calls to the db on behalf of a user.
Based on my reading, we have to figure out which approach we want to take with respect to how we access data. If we do it from our microservices, we can use the service role key
and it's easy. If, however, we want to do things from the browser directly, we'll have to add another JWT in the client-side code. So our auth provider in the client would hold a JWT for Telescope's microservices, and another for Supabase.
We could also merge the tokens, or choose to use the Supabase token as the same token our microservices accept. This might be the best way to go. I think it would end up looking like this:
The token we send back in step 4 is currently created by us, but we could use the one from Supabase instead. We'd just have to adjust some of our middleware for checking user roles (e.g., is this an admin). To be honest, that code is not very complex, so it's likely pretty easy to use the policies in the Supabase tokens.
In https://github.com/Seneca-CDOT/telescope/issues/2837 we need to make use of the Server-side auth API, see https://supabase.com/docs/reference/javascript/auth-api-createuser. I'll comment in that issue instead of here.
@joelazwar this probably affects your work on JWT signing. Read this stuff and see what you think, since Supabase seems to only sign with a secret.
Did some reading, couldn't find anything on RSA or public/private keys for JWTs in Supabase. I definitely agree with you though, let's use Supabase's token issuer to supply the JWT for all our services.
I'll update this with more details, so @joelazwar can finish it.
In #2844, we have the bulk of the work to get this started, but it's missing a critical piece: we need to be able to connect to Supabase from a browser without exposing the Supabase service role key (only appropriate on a server, where it can be protected). Our SSO service now uses the service role key in @DukeManh's PR to create rows in the telescope_profiles
table. Here's what I think needs to happen next, based on the research Duke and I have done.
SECRET
variable here https://github.com/Seneca-CDOT/telescope/blob/master/docker/docker-compose.yml#L98, and we need to switch this to use JWT_SECRET
. Doing so will mean we can share this same secret between all of our backends (microservices, Supabase).JWT_SECRET
in the SSO service, we need to use it to sign the token we create for a client in https://github.com/Seneca-CDOT/telescope/blob/master/src/api/sso/src/token.js#L51. This will mean that we've signed our token with the same secret that Supabase (i.e., Kong) expects to see when a request comes in.docker/docker-compose.yml
file, for example https://github.com/Seneca-CDOT/telescope/blob/master/docker/docker-compose.yml#L23-L26. Instead of using SECRET
, we need to forward JWT_SECRET
into these microservice's environments. This change needs to happen for every service.SECRET
environment variable, see https://github.com/Seneca-CDOT/satellite#configure. We need to modify this to JWT_SECRET
, and update all uses of SECRET
in the code, see https://github.com/Seneca-CDOT/satellite/search?q=SECRET. We then need to ship a new release of Satellite with these changes.client
with the JWT we get back from the SSO service. We would do that by calling supabase.auth.setAuth()
in the front-end when we get back the JWT from the SSO service, maybe here: https://github.com/Seneca-CDOT/telescope/blob/master/src/web/src/components/AuthProvider.tsx#L120-L122. We might decide to return a Supabase client
object with the useAuth()
hook, so callers can use it directly without having to figure out this authentication step.supabase.auth.setAuth(token)
and then use the authorized client to try doing some call to the Supabase API, and make sure it works. An example might be: update the user's name in the profiles table.telescope_profiles
and feeds
tables (as well as any others we create). See https://blog.crunchydata.com/blog/a-postgresql-row-level-security-primer-creating-large-policies or other tutorials on how RLS works. If you run our containers now, you can go to http://localhost:8910 to see the Supabase console. Click on "Default Project" to see our Telescope project, then on the "Table editor" to see our tables, or "Authentication" to see our security setup. At http://localhost:8910/project/default/auth/policies you can see our Policies for each table. Currently, RLS isn't enabled for either table, but we can click Enable RLS to add it, and then click "New Policy" to define a policy. There are templates we can use to write these. For us, an example might be: "Users can only modify/delete records in feeds if the id
in the JWT matches the id
of the owner of the feed." We'll need to manually add these policies to our .sql
so they get created automatically, but the GUI is nice for experimenting.This is all that I can think of at the moment, but it should get you going, @joelazwar. Please jump into this and start knocking out PRs. Don't try to do it all in one big PR.
If you have questions, talk to @DukeManh and myself.
Thanks! this is really comprehensive. Will get to work on it and get a PR asap.
What happened:
Part of #2555, once we config supabase and have it running, we want to make the
Auth
service communicate with the Supabase through the supabase-js package to handle new user registration and login.How to reproduce it (as precise as possible):