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

如何将OpenID Connect声明纳入Firebase登录属性?

问题:将Azure AD OIDC声明映射到Firebase ID Token的sign_in_attributes

我们使用谷歌身份平台作为身份提供商,目前支持Email/Password、SAML(SSO)两种认证方式,仅用于身份验证。用户认证后,后端验证ID Token有效性,再用自有密钥集签发JWT,并附加租户ID这类自定义客户端ID数据。

现在计划新增OpenID Connect认证方式(对接Azure AD,即Microsoft Entra),认证流程已可运行,但需要将Azure AD返回的given_name、family_name字段纳入现有流程。当前前端流程代码如下:

// 已预配置Scope为"openid profile email"
const provider = new OAuthProvider(ssoId);
await signInWithRedirect($auth, provider);

用户认证后有两种处理选择:

  • 获取Firebase ID Token发送至后端,但该Token缺少邮箱、姓名等信息;
  • 提取微软ID Token,其中包含given_name和family_name:
const userCredential = await getRedirectResult($auth);
if (userCredential) {
  const credentials = OAuthProvider.credentialFromResult(userCredential);
  console.log(credentials); // 微软返回的Access Token + ID Token
  console.log(userCredential); // 解码后的Firebase ID Token
}

我们希望沿用SAML认证的流程,将这些字段映射到Firebase的sign_in_attributes中,但当前Firebase ID Token的sign_in_attributes里缺少所需信息,示例如下:

{
  "name": "*redacted*",
  "iss": "https://securetoken.google.com/*redacted*-dev",
  "aud": "*redacted*-dev",
  "auth_time": 1695129924,
  "user_id": "zLqx1u73QaU9uOmkaNvEZZbkNh42",
  "sub": "zLqx1u73QaU9uOmkaNvEZZbkNh42",
  "iat": 1695129924,
  "exp": 1695133524,
  "firebase": {
    "identities": {
      "oidc.*redacted*-azure-ad": [
        "VYmuzDUMQUlSkGnNLHVDQ-vxdvV9qJDhTEMJamqqzos"
      ]
    },
    "sign_in_provider": "oidc.*redacted*-azure-ad",
    "sign_in_attributes": {
      "ver": "2.0",
      "aio": "AWQAm/8UAAAApsgZB1l8/pUKK3DdCTzioVEHpJOT41hs7XiATk37ShUwjgYCa0HZIOJmOi2GgswENS+u6Sb3y7DVwqR2Na+6JBgiufC24Hn8VMJnP9o2u7fk8/xigzNRjs8S8Qydc4R9",
      "rh": "0.ATkAcY6U2Y_y7kWFt7vUytrJub9-rLRmV_5MvduzlkIsCrzgAJs.",
      "oid": "158e8a64-5d48-4641-bf76-7630a0aa79cd",
      "preferred_username": "*redacted*@*redacted*.onmicrosoft.com",
      "uti": "PsyrhHx2bk294jOC7FgPAA",
      "tid": "d9948e71-f28f-45ee-85b7-bbd4cadac9b9"
    },
    "tenant": "*redacted*-test-*redacted*-gnh1z"
  }
}

现咨询:如何将OpenID Connect ID Token中的指定声明纳入Firebase的sign_in_attributes?若无法实现,推荐的替代流程是什么?


解决方案

一、尝试将OIDC声明映射到Firebase sign_in_attributes

Firebase对OIDC提供商的sign_in_attributes填充逻辑是固定的,无法像SAML那样自定义映射规则,但可以尝试以下两种方式:

  1. 强制Azure AD输出指定声明
    在Azure AD应用注册的「令牌配置」中,添加given_name、family_name作为可选声明,强制要求这些字段包含在ID Token中。虽然已请求profile scope,但Azure AD默认可能不会将所有profile字段写入ID Token,手动配置可选声明有可能让Firebase在处理时同步这些字段到sign_in_attributes(此方法不保证100%生效,取决于Firebase的OIDC属性提取规则)。

  2. 通过Firebase云函数写入自定义Claims
    触发用户登录/创建的云函数(onSignIn或onCreate),从微软ID Token中解析出given_name和family_name,调用Firebase Admin SDK的updateUser方法将这些字段写入用户的自定义Claims。自定义Claims会出现在Firebase ID Token的顶层(而非sign_in_attributes内),但后端可直接读取,能达到类似SAML流程的效果。

二、替代流程推荐

如果上述方法无法实现,推荐以下两种兼容现有后端流程的方案:

方案1:前端同时发送Firebase ID Token和微软ID Token

前端获取到userCredential后,同时提取Firebase ID Token和微软ID Token,一起发送给后端。后端先验证Firebase ID Token的有效性,再解码微软ID Token提取given_name、family_name,最后签发自有JWT时附加这些数据。

优点:无需改动Firebase配置,逻辑直接;
缺点:前端多传一个Token,后端需额外验证微软ID Token的签名(可使用Azure AD公钥验证)。

前端代码示例:

const userCredential = await getRedirectResult($auth);
if (userCredential) {
  const credentials = OAuthProvider.credentialFromResult(userCredential);
  const firebaseIdToken = await userCredential.user.getIdToken();
  // 同时发送两个Token到后端
  await fetch('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      firebaseIdToken,
      microsoftIdToken: credentials.idToken
    })
  });
}

方案2:后端通过Firebase Admin SDK+Azure AD Graph API补充字段

前端仅发送Firebase ID Token到后端,后端验证后通过Firebase Admin SDK获取用户的provider数据,若其中不包含given_name和family_name,再调用Azure AD Graph API,根据用户的OIDC身份ID获取完整profile信息。

优点:前端改动小,后端统一处理;
缺点:需后端集成Azure AD Graph API,增加依赖。

后端代码示例(Node.js):

const admin = require('firebase-admin');
admin.initializeApp();

async function handleLogin(req, res) {
  const { firebaseIdToken } = req.body;
  
  // 验证Firebase ID Token
  const decodedToken = await admin.auth().verifyIdToken(firebaseIdToken);
  // 获取用户的provider数据
  const user = await admin.auth().getUser(decodedToken.uid);
  const microsoftProvider = user.providerData.find(p => p.providerId === 'oidc.your-azure-ad-provider-id');

  let givenName, familyName;
  if (microsoftProvider) {
    // 先从providerData中提取,没有则调用Graph API
    givenName = microsoftProvider.givenName;
    familyName = microsoftProvider.familyName;
    
    if (!givenName || !familyName) {
      // 调用Azure AD Graph API获取用户信息
      const graphResponse = await fetch(`https://graph.microsoft.com/v1.0/users/${microsoftProvider.uid}`, {
        headers: { 'Authorization': `Bearer ${yourAzureADAccessToken}` }
      });
      const graphUser = await graphResponse.json();
      givenName = graphUser.givenName;
      familyName = graphUser.familyName;
    }
  }

  // 签发自有JWT并附加字段
  const customJwt = require('jsonwebtoken').sign({
    userId: decodedToken.uid,
    tenantId: decodedToken.firebase.tenant,
    given_name: givenName,
    family_name: familyName
  }, process.env.JWT_SECRET, { expiresIn: '1h' });

  res.json({ token: customJwt });
}

内容的提问来源于stack exchange,提问作者nook

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 17:17:31