Firebase 认证
在您的 Supabase 项目中使用 Firebase 认证
Firebase Auth 可以作为第三方认证提供商与 Supabase Auth 一起使用,也可以独立用于您的 Supabase 项目。
开始使用
- 首先需要添加集成来连接您的 Supabase 项目与 Firebase 项目。您需要在 Firebase 控制台获取项目 ID。
- 在项目的认证设置中添加新的第三方认证集成。
- 如果您在自托管时使用第三方认证,请为 public 模式下的所有表、Storage 和 Realtime 创建并附加限制性 RLS 策略,以防止来自无关 Firebase 项目的未授权访问。
- 为所有用户分配
role: 'authenticated'
自定义用户声明。 - 最后在您的应用程序中设置 Supabase 客户端。
设置 Supabase 客户端库
创建 Web 客户端非常简单,只需传递 accessToken
异步函数即可。该函数应返回当前用户的 Firebase Auth JWT(如果找不到用户则返回 null)。
1234567import { createClient } from '@supabase/supabase-js'const supabase = createClient('https://<supabase-project>.supabase.co', 'SUPABASE_ANON_KEY', { accessToken: async () => { return (await firebase.auth().currentUser?.getIdToken(/* forceRefresh */ false)) ?? null },})
请确保应用程序中的所有用户都设置了 role: 'authenticated'
自定义声明。如果使用 onCreate
云函数为新注册用户添加此自定义声明,由于 onCreate
函数不会同步运行,注册后需要立即调用 getIdToken(/* forceRefresh */ true)
。
为项目添加新的第三方认证集成
在仪表板中,导航至您项目的认证设置,找到"第三方认证"部分来添加新的集成。
在CLI中,将以下配置添加到您的supabase/config.toml
文件:
123[auth.third_party.firebase]enabled = trueproject_id = "<id>"
为项目的RLS策略添加额外安全层(仅限自托管)
请仔细遵循本节内容,以防止在自托管时未经授权访问您的项目数据。
当使用Supabase托管平台时,此步骤是可选的。
Firebase Auth对所有项目使用同一组JWT签名密钥。这意味着来自与您项目无关的Firebase项目签发的JWT可能会访问您Supabase项目中的数据。
使用Supabase托管平台时,来自未注册Firebase项目ID的JWT在到达您的数据库之前就会被拒绝。而在自托管时,实现这一机制是您的责任。一个简单的防护方法是为**public
模式中的所有表**创建并维护以下RLS策略。您还应将此策略应用于存储桶或实时频道。
建议您使用限制性Postgres行级安全策略。
限制性RLS策略与常规(或宽松)策略的不同之处在于,它们在定义时使用as restrictive
子句。它们不授予权限,而是限制任何现有或未来的权限。对于像Firebase Auth技术限制与应用程序逻辑分离的情况非常适用。
Postgres有两种策略类型:宽松型和限制型。此示例使用限制型策略,因此请确保不要省略as restrictive
子句。
以下是一个RLS策略示例,它将限制仅允许您项目(用<firebase-project-id>
表示)的用户访问,而不允许其他任何Firebase项目访问。
12345678910111213create policy "限制仅Supabase Auth和Firebase Auth项目ID <firebase-project-id>可访问" on table_name as restrictive to authenticated using ( (auth.jwt()->>'iss' = 'https://<project-ref>.supabase.co/auth/v1') or ( auth.jwt()->>'iss' = 'https://securetoken.google.com/<firebase-project-id>' and auth.jwt()->>'aud' = '<firebase-project-id>' ) );
如果您的应用中有许多表,或者需要为存储或实时管理复杂的RLS策略,定义一个稳定的Postgres函数来执行检查可以减少重复代码。例如:
1234567891011121314create function public.is_supabase_or_firebase_project_jwt() returns bool language sql stable returns null on null input return ( (auth.jwt()->>'iss' = 'https://<project-ref>.supabase.co/auth/v1') or ( auth.jwt()->>'iss' = concat('https://securetoken.google.com/<firebase-project-id>') and auth.jwt()->>'aud' = '<firebase-project-id>' ) );
请确保将<project-ref>
替换为您的Supabase项目ID,将<firebase-project-id>
替换为您的Firebase项目ID。然后所有表、桶和频道上的限制性策略可以简化为:
12345create policy "限制仅正确的Supabase和Firebase项目可访问" on table_name as restrictive to authenticated using ((select public.is_supabase_or_firebase_project_jwt()) is true);
分配"role"自定义声明
您的Supabase项目会检查所有发送给它的JWT中存在的role
声明,以便在使用数据API、存储或实时功能授权时分配正确的Postgres角色。
默认情况下,Firebase的JWT中不包含role
声明。如果您将这样的JWT发送到您的Supabase项目,在执行Postgres查询时将分配anon
角色。您应用的大部分逻辑应该由authenticated
角色访问。
使用 Firebase 认证函数分配认证角色
根据您的 Firebase 项目配置,有两种设置 Firebase 认证函数的方式:
- 最简单方式:使用 Firebase 阻塞式认证函数,但此功能仅适用于使用 Firebase 认证与 Identity Platform 的项目。
- 手动方式:使用 admin SDK 为所有用户分配自定义声明,并定义
onCreate
Firebase 认证云函数 来为新创建的用户持久化角色。
123456789101112131415161718192021import { beforeUserCreated, beforeUserSignedIn } from 'firebase-functions/v2/identity'export const beforecreated = beforeUserCreated((event) => { return { customClaims: { // Supabase 项目将使用此角色分配 `authenticated` // Postgres 角色 role: 'authenticated', }, }})export const beforesignedin = beforeUserSignedIn((event) => { return { customClaims: { // Supabase 项目将使用此角色分配 `authenticated` // Postgres 角色 role: 'authenticated', }, }})
注意:您可以使用 sessionClaims
替代 customClaims
。区别在于 session_claims
不会保存在 Firebase 用户配置文件中,但在用户登录期间保持有效。
最后部署您的函数使更改生效:
1firebase deploy --only functions
请注意,这些函数仅在用户新注册和登录时调用。现有用户的 ID 令牌中不会包含这些声明。您需要使用 admin SDK 为所有用户分配角色自定义声明。请确保在部署上述 Firebase 阻塞式认证函数后再执行此操作。
使用 Admin SDK 为所有用户分配角色自定义声明
您需要运行一个脚本,为所有现有的 Firebase Authentication 用户分配 role: 'authenticated'
自定义声明。这可以通过结合使用列出用户和设置自定义用户声明这两个管理 API 来实现。下面提供了一个示例脚本:
1234567891011121314151617181920212223242526'use strict';const { initializeApp } = require('firebase-admin/app');const { getAuth } = require('firebase-admin/auth');initializeApp();async function setRoleCustomClaim() => { let nextPageToken = undefined do { const listUsersResult = await getAuth().listUsers(1000, nextPageToken) nextPageToken = listUsersResult.pageToken await Promise.all(listUsersResult.users.map(async (userRecord) => { try { await getAuth().setCustomUserClaims(userRecord.id, { role: 'authenticated' }) } catch (error) { console.error('设置用户自定义角色失败', userRecord.id) } }) } while (nextPageToken);};setRoleCustomClaim().then(() => process.exit(0))
当所有用户都获得 role: 'authenticated'
声明后,该声明将出现在用户新颁发的所有 ID 令牌中。