You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 20:05:17