Firebase Auth首次SAML登录后displayName被置为undefined问题
问题描述
在Next.js Web应用中使用Firebase Authentication对接Azure SSO的SAML提供商时,遇到以下问题:用户首次登录后,Firebase Authentication中的displayName属性被设置为undefined。
使用的库版本
"firebase": "^10.7.1", "firebase-admin": "^12.1.1",
用户创建代码
import * as admin from 'firebase-admin' export function getFirebaseAdmin() { if (!admin.apps.length) { admin.initializeApp(/**appoptions**/) } return admin } // 新建用户代码 await getFirebaseAdmin().auth().createUser({ uid: id, email: email.toLowerCase(), displayName: displayName, // 此处传入的是有效值 emailVerified: false, disabled: false, })
首次登录后的异常现象
const userRecord = await getFirebaseAdmin().auth().getUser('USERIDXXXX') console.log('User', userRecord.displayName) // 输出undefined
前端登录代码及结果
const samlProvider = new SAMLAuthProvider('saml.myid') const userCredential: UserCredential = await signInWithPopup(getAuth(/**app**/), samlProvider) console.log(userCredential.user.displayName) // 输出undefined console.log(userCredential.user.providerData[0].displayName) // 输出undefined
已尝试的操作及SAML声明情况
已在Azure端SSO配置中添加displayName声明,但未生效。通过SAML tracer捕获到的声明内容如下:
<Attribute Name="displayName"> <AttributeValue>User Display Name</AttributeValue> </Attribute> <Attribute Name="displayname"> <AttributeValue>User Display Name</AttributeValue> </Attribute>
特殊场景说明
- Firebase仅在首次登录后删除
displayName属性; - 若首次登录后手动重置
displayName,后续登录不会再覆盖该属性:
const samlProvider = new SAMLAuthProvider('saml.myid') const userCredential: UserCredential = await signInWithPopup(getAuth(/**app**/), samlProvider) console.log(userCredential.user.displayName) // 保持手动设置的值 console.log(userCredential.user.providerData[0].displayName) // 仍为undefined
解决方案
1. 配置SAML声明的正确命名空间
Firebase对SAML声明的识别需要绑定特定的URN命名空间,仅使用displayName作为属性名无法被正确识别。需在Azure的SAML配置中调整声明:
- 在Azure门户的企业应用SAML配置页面,进入「用户属性与声明」编辑界面;
- 添加或修改声明,将
Name设置为http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name,值选择用户的显示名称字段; - 确保SAML响应中包含该带正确URN的属性,而非仅使用普通的
displayName属性名。
同时,需在Firebase控制台的SAML提供商配置页,找到「属性映射」部分,将Azure发送的上述声明映射到Firebase的displayName用户属性。
2. 通过云函数拦截首次登录事件,保留原始displayName
利用Firebase Auth云函数,在用户创建时备份原始displayName,首次登录时若被重置则恢复:
import * as functions from "firebase-functions"; import * as admin from "firebase-admin"; admin.initializeApp(); // 用户创建时备份displayName到自定义属性 exports.backupDisplayName = functions.auth.user().onCreate(async (user) => { if (user.displayName) { await admin.auth().updateUser(user.uid, { customClaims: { originalDisplayName: user.displayName } }); } }); // 首次登录时恢复displayName exports.restoreDisplayName = functions.auth.user().onSignIn(async (user) => { const userRecord = await admin.auth().getUser(user.uid); if (!userRecord.displayName && userRecord.customClaims?.originalDisplayName) { await admin.auth().updateUser(user.uid, { displayName: userRecord.customClaims.originalDisplayName }); // 可选:恢复后清除自定义属性备份 await admin.auth().setCustomUserClaims(user.uid, {}); } });
3. 前端手动处理登录后的属性修复
在前端登录成功后,检查displayName是否为undefined,若缺失则调用接口获取预设值并更新:
// 登录完成后执行 const userCredential = await signInWithPopup(getAuth(app), samlProvider); const user = userCredential.user; if (!user.displayName) { // 从后端接口获取用户预设的displayName const res = await fetch('/api/get-user-displayname', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ uid: user.uid }) }); const displayName = await res.json(); if (displayName) { await updateProfile(user, { displayName }); } }
内容的提问来源于stack exchange,提问作者lordofmax
相关产品推荐
相关产品推荐

