使用自定义JWT时Supabase实时连接失败问题求助
自托管Supabase结合Auth0认证时Realtime订阅的JWT认证问题
问题描述
我使用自托管Supabase,采用Auth0进行认证而非Supabase默认认证方案。我用Supabase密钥对包含用户ID的负载进行签名,并将生成的JWT放在请求头Authorization中,普通数据库查询(已开启RLS策略)可正常执行,但创建Realtime订阅时,WebSocket连接抛出认证错误;若不携带自定义JWT则连接正常,但我需要将自定义JWT的payload传入Realtime订阅,以便在RLS策略中使用。
当前实现代码:
const payload = { userId: user.email, exp: Math.floor(Date.now() / 1000) + 60 * 60, } const accessToken = jwt.sign(payload, SUPABASE_SECRET_KEY) const options = {} if (accessToken) { options.global = { headers: { Authorization: `Bearer ${accessToken}`, }, } } const supabase = createClient(supabaseUrl, supabaseAnonKey, options) supabase.channel('custom-update-channel') .on( 'postgres_changes', { event: 'UPDATE', schema: 'public', table: 'user_notifications' }, (payload) => { console.log('Change received!', payload) } ) .subscribe()
解决方案
1. 补全自定义JWT的必要字段
Realtime服务对JWT有额外字段要求,除了exp(过期时间),必须包含iat(签发时间)字段,同时建议添加sub字段(Realtime内部依赖此字段做身份关联)。修改JWT生成代码:
const payload = { userId: user.email, sub: user.email, // 与userId保持一致即可 exp: Math.floor(Date.now() / 1000) + 60 * 60, iat: Math.floor(Date.now() / 1000) // 新增签发时间字段 } const accessToken = jwt.sign(payload, SUPABASE_SECRET_KEY)
2. 配置自托管Realtime的JWT验证规则
自托管Supabase的Realtime默认会校验JWT的iss(签发者)字段,需要在Realtime服务配置中适配自定义JWT:
- 打开自托管环境的
docker-compose.yml文件,找到realtime服务的environment配置段 - 添加
REALTIME_JWT_ISS=your-auth0-domain(替换为你的Auth0域名,比如https://your-auth0-domain.auth0.com/),如果是测试环境可临时设置REALTIME_JWT_ALLOW_ANY=true(不推荐生产环境使用) - 重启Realtime服务使配置生效
3. 显式为Realtime连接传递JWT
Supabase JS客户端的全局header设置不会自动同步到WebSocket连接,需要在订阅时显式传递认证信息:
supabase.channel('custom-update-channel') .on( 'postgres_changes', { event: 'UPDATE', schema: 'public', table: 'user_notifications' }, (payload) => { console.log('Change received!', payload) } ) .subscribe({ auth: { headers: { Authorization: `Bearer ${accessToken}` } } })
4. 确保RLS策略兼容Realtime角色
Realtime服务使用realtime角色访问数据库,需要在RLS策略中包含该角色:
CREATE POLICY "User can access their own notifications" ON public.user_notifications FOR ALL TO authenticated, realtime USING ( (current_setting('request.jwt.claims', true)::json->>'userId') = email ) WITH CHECK ( (current_setting('request.jwt.claims', true)::json->>'userId') = email );
内容的提问来源于stack exchange,提问作者Goutham Raj R
相关产品推荐
相关产品推荐

