Use the Supabase Service Key to Bypass Row Level Security

Jon Meyers
InstructorJon Meyers

Share this video with your friends

Send Tweet
Published a month ago
Updated 8 hours ago

Stripe will call our /stripe-hooks API route anytime a subscription is created, updated, or deleted. By inspecting the event.type, we can specify what we want to do for each type of event. We will create a switch statement to process events from Stripe. If we are creating a subscription, we want to set is_subscribed to true.

This request to Supabase is happening on the server and does not have a session automatically attached. Since this endpoint is actually called by Stripe, we don't have a cookie that we can manually send along, so our request to update the user is being denied by RLS.

RLS can be bypassed by using Supabase's Service key. We can export a function from our utils/supabase.js file that returns a Supabase client - instantiated using the environment variable for the service key, rather than the anon key. Since this environment variable is only available server-side, this value will not be leaked to the client, and our database is still securely protected with RLS.

Instructor: [0:00] Each of our webhook events from Stripe has a type which maps to the event's name. Let's create a switch statement to work out which event type we're currently processing. For the case of customer.subscription.created, we want to tell Supabase to update our user's profile.

[0:16] Let's import our Supabase client, and then we can say supabase.from profile we would like to update the column, isSubscribed to true. We want to do that where the Stripe customer column equals event.data.object.customer. Let's trigger our webhook again.

[0:35] If we open up our Supabase database and refresh, we'll see that our column has still not changed. This is happening because we've only written a policy for select. Currently, every update request is automatically being denied. Since this is happening in an API route that's being triggered by Stripe, we don't really have an associated user who's performing this action.

[0:55] In cases like this, we can bypass RLS all together by using our service key rather than our anon key. Let's copy that value into our .env file. We'll need to restart our development server to read in that new value.

[1:12] In our utils/Supabase file, we can export a function called getServiceSupabase. This is also going to create a client using the public Supabase URL. Then as the key, we're going to use process.mf.Supabase Service key.

[1:28] This might seem like we're creating a function that could potentially be called by the frontend and get a Supabase Client that's allowed to do anything. Remember, this environment variable is only available server-side.

[1:39] Even if the client tried to call this function, this value would be null and the call to create client would fail. We can now import this function in our Stripe hooks file and call it in our handler.

[1:53] If we trigger our webhook and check our profile table in Supabase, we'll see our isSubscribed column has been updated to true. We also want to set our interval to whether this is yearly or monthly. We can do that by setting it to event.data.object.items.data indexzero.plan.interval.

[2:18] If we set our isSubscribed column back to false and trigger our webhook, we can refresh our profile table and see isSubscribed is set to true and our interval is month.

[2:30] Lastly, because our API route that creates a Stripe customer for us is also happening server-side, we need to update it to also use the service level Supabase Client before we make a request to update the profile. Again, anytime we want to bypass low-level security, we can use our service as Superbase Client instead of our regular Superbase Client.