Firebase Auth
Use Firebase Auth with your Supabase project
Creating a Firebase Auth third-party integration with your Supabase project is currently limited to a private alpha release. We are still trying to improve the developer experience around securing your project. Register your interest in Third-Party Auth with Firebase and the team will reach out to you.
Firebase Auth can be used as a third-party authentication provider alongside Supabase Auth, or standalone, with your Supabase project.
Getting started
- First you need to add an integration to connect your Supabase project with your Firebase project. You will need to get the Project ID in the Firebase Console.
- Add a new Third-party Auth integration in your project's Authentication settings.
- If you are using Third Party Auth in local development, create and attach restrictive RLS policies to all tables in your public schema, Storage and Realtime to prevent unauthorized access from unrelated Firebase projects.
- Assign the
role: 'authenticated'
custom user claim to all your users. - Finally set up the Supabase client in your application.
Setup the Supabase client library
Creating a client for the Web is as easy as passing the accessToken
async function. This function should return the Firebase Auth JWT of the current user (or null if no such user) is found.
_10import { createClient } from '@supabase/supabase-js'_10_10const supabase = createClient('https://<supabase-project>.supabase.co', 'SUPABASE_ANON_KEY', {_10 accessToken: async () => {_10 ;(await firebase.auth().currentUser?.getIdToken(/* forceRefresh */ false)) ?? null_10 },_10})
Make sure the all users in your application have the role: 'authenticated'
custom claim set. If you're using the onCreate
Cloud Function to add this custom claim to newly signed up users, you will need to call getIdToken(/* forceRefresh */ true)
immediately after sign up as the onCreate
function does not run synchronously.
Add a new Third-Party Auth integration to your project
In the dashboard navigate to your project's Authentication settings and find the Third-Party Auth section to add a new integration.
In the CLI add the following config to your supabase/config.toml
file:
_10[auth.third_party.firebase]_10enabled = true_10project_id = "<id>"
Adding an extra layer of security to your project's RLS policies
While Third-Party Auth with Firebase is in a private alpha, it is strongly recommended to follow this guide closely. Once it becomes generally available this method would not be necessary to secure your project against unauthorized access from other Firebase projects.
You should also follow this best practice if self-hosting.
Firebase Auth uses a single set of JWT signing keys for all projects. This means that JWTs issued from an unrelated Firebase project to yours could access data in your Supabase project. To guard against this, creating and maintaining the following RLS policies for all of your tables in the public
schema is very important. You should also attach this policy to Storage buckets or Realtime channels.
To achieve this we recommend using a restrictive Postgres Row-Level Security policy.
Restrictive RLS policies differ from regular (or permissive) policies in that they use the as restrictive
clause when being defined. They do not grant permissions, but rather restrict any existing or future permissions. They're great for cases like this where the technical limitations of Firebase Auth remain separate from your app's logic.
Postgres has two types of policies: permissive and restrictive. This example uses restrictive policies so make sure you don't omit the as restrictive
clause.
This is an example of such an RLS policy that will restrict access to only your project's (denoted with <firebase-project-id>
) users, and not any other Firebase project.
_13create policy "Restrict access to Supabase Auth and Firebase Auth for project ID <firebase-project-id>"_13 on table_name_13 as restrictive_13 to authenticated_13 using (_13 (auth.jwt()->>'iss' = 'https://<project-ref>.supabase.co/auth/v1')_13 or_13 (_13 auth.jwt()->>'iss' = 'https://securetoken.google.com/<firebase-project-id>'_13 and_13 auth.jwt()->>'aud' = '<firebase-project-id>'_13 )_13 );
If you have a lot of tables in your app, or need to manage complex RLS policies for Storage or Realtime it can be useful to define a stable Postgres function that performs the check to cut down on duplicate code. For example:
_14create function public.is_supabase_or_firebase_project_jwt()_14 returns bool_14 language sql_14 stable_14 returns null on null input_14 return (_14 (auth.jwt()->>'iss' = 'https://<project-ref>.supabase.co/auth/v1')_14 or_14 (_14 auth.jwt()->>'iss' = concat('https://securetoken.google.com/<firebase-project-id>')_14 and_14 auth.jwt()->>'aud' = '<firebase-project-id>'_14 )_14 );
Make sure you substitute <project-ref>
with your Supabase project's ID and the <firebase-project-id>
to your Firebase Project ID. Then the restrictive policies on all your tables, buckets and channels can be simplified to be:
_10create policy "Restrict access to correct Supabase and Firebase projects"_10 on table_name_10 as restrictive_10 to authenticated_10 using ((select public.is_supabase_or_firebase_project_jwt()) is true);
Assign the "role" custom claim
Your Supabase project inspects the role
claim present in all JWTs sent to it, to assign the correct Postgres role when using the Data API, Storage or Realtime authorization.
By default, Firebase JWTs do not contain a role
claim in them. If you were to send such a JWT to your Supabase project, the anon
role would be assigned when executing the Postgres query. Most of your app's logic will be accessible by the authenticated
role.
Use Firebase Authentication functions to assign the authenticated role
You have two choices to set up a Firebase Authentication function depending on your Firebase project's configuration:
- Easiest: Use a blocking Firebase Authentication function but this is only available if your project uses Firebase Authentication with Identity Platform.
- Manually assign the custom claims to all users with the admin SDK and define an
onCreate
Firebase Authentication Cloud Function to persist the role to all newly created users.
_21import { beforeUserCreated, beforeUserSignedIn } from 'firebase-functions/v2/identity'_21_21export const beforecreated = beforeUserCreated((event) => {_21 return {_21 customClaims: {_21 // The Supabase project will use this role to assign the `authenticated`_21 // Postgres role._21 role: 'authenticated',_21 },_21 }_21})_21_21export const beforesignedin = beforeUserSignedIn((event) => {_21 return {_21 customClaims: {_21 // The Supabase project will use this role to assign the `authenticated`_21 // Postgres role._21 role: 'authenticated',_21 },_21 }_21})
Note that instead of using customClaims
you can instead use sessionClaims
. The difference is that session_claims
are not saved in the Firebase user profile, but remain valid for as long as the user is signed in.
Finally deploy your functions for the changes to take effect:
_10firebase deploy --only functions
Note that these functions are only called on new sign-ups and sign-ins. Existing users will not have these claims in their ID tokens. You will need to use the admin SDK to assign the role custom claim to all users. Make sure you do this after the blocking Firebase Authentication functions as described above are deployed.
Use the admin SDK to assign the role custom claim to all users
You need to run a script that will assign the role: 'authenticated'
custom claim to all of your existing Firebase Authentication users. You can do this by combining the list users and set custom user claims admin APIs. An example script is provided below:
_26'use strict';_26const { initializeApp } = require('firebase-admin/app');_26const { getAuth } = require('firebase-admin/auth');_26initializeApp();_26_26async function setRoleCustomClaim() => {_26 let nextPageToken = undefined_26_26 do {_26 const listUsersResult = await getAuth().listUsers(1000, nextPageToken)_26_26 nextPageToken = listUsersResult.pageToken_26_26 await Promise.all(listUsersResult.users.map(async (userRecord) => {_26 try {_26 await getAuth().setCustomUserClaims(userRecord.id, {_26 role: 'authenticated'_26 })_26 } catch (error) {_26 console.error('Failed to set custom role for user', userRecord.id)_26 }_26 })_26 } while (nextPageToken);_26};_26_26setRoleCustomClaim().then(() => process.exit(0))
After all users have received the role: 'authenticated'
claim, it will appear in all newly issued ID tokens for the user.