使用 SAML 登录用户

本文档介绍如何使用 Identity Platform 让用户通过安全断言标记语言 (SAML) 2.0 提供方登录。

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  5. 确保您的 Google Cloud 项目已启用结算功能

  6. 启用 Identity Platform,并将 Client SDK 添加到您的应用。如需了解具体方法,请参阅快速入门

配置提供商

  1. 前往 Google Cloud 控制台中的身份提供商页面。
    转到“身份提供商”页面

  2. 点击添加提供商,然后从列表中选择 SAML

  3. 输入以下详细信息:

    1. 提供商的名称。它可以与提供商 ID 相同,也可以是自定义名称。如果要输入自定义名称,请点击提供商 ID 旁边的修改以指定 ID(必须以 saml. 开头)。

    2. 提供商的实体 ID

    3. 提供商的 SAML SSO 网址

    4. 用于对提供商进行令牌签名的证书。请确保包含开始字符串和结束字符串。例如:

      -----BEGIN CERTIFICATE-----
      MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czEL
      ...
      LEzc1JwEGQQVDYQCwsQMSBDAF0QAB0w9GikhqkgBNADABIgABIwAgOdACCjaCIIM
      -----END CERTIFICATE-----
      
  4. 服务提供商下,输入应用的实体 ID。这通常是您的应用的网址。SAML 身份提供商称之为受众。

  5. 将您的应用添加到已获授权的网域列表中。例如,如果应用的登录网址为 https://example.com/login,请添加 example.com

  6. 如有需要,您可以为应用自定义回调网址。SAML 身份提供商通常称之为断言消费者服务 (ACS) 网址。

    使用默认回调网址可以降低验证 SAML 响应的复杂程度。如果您要自定义此流程,请确保为 SAML 身份提供商配置项目的 Identity Platform 回调网址。它的格式通常为 https://[PROJECT-ID].firebaseapp.com/__/auth/handler。如需了解详情,请参阅自定义身份验证处理程序

  7. 点击保存

提供方必需的元素

Identity Platform 需要提供商的响应中包含 <saml:Subject><saml:NameID> 元素。 如果您在配置提供程序时未为这些元素定义值,SAML 断言将会失败。

对请求进行签名

您可以通过对身份验证请求进行签名来增强其安全性。

要对请求进行签名,首先需要为您的身份提供商启用签名请求,方法是调用 inboundSamlConfigs.patch() 并将 idp_config.sign_request 设置为 true

REST

在使用任何请求数据之前,请先进行以下替换:

  • project-id:Google Cloud 项目的 ID
  • provider-id:SAML 提供方 ID

HTTP 方法和网址:

PATCH https://identitytoolkit.googleapis.com/admin/v2/projects/project-id/inboundSamlConfigs/provider-id?updateMask=idpConfig.signRequest

请求 JSON 正文:

{
  "idp_config": {
    "sign_request": true
  }
}

如需发送您的请求,请展开以下选项之一:

 

您必须使用 REST API 启用已签名的请求;使用 不支持 Google Cloud 控制台或 Google Cloud CLI。

响应是一个 InboundSamlConfig 对象,它包含一组 SpCertificate。 使用您的 SAML 身份提供商配置 X509 证书的值,以便它能够验证请求的签名。

登录用户

当用户登录时,Client SDK 会处理身份验证握手,然后返回载荷中包含 SAML 特性的 ID 令牌。要登录用户并从 SAML 提供商获取特性,请执行以下操作:

  1. 使用您在上一部分中配置的提供商 ID 创建 SAMLAuthProvider 实例。提供商 ID 必须以 saml. 开头。

    Web 版本 9

    import { SAMLAuthProvider } from "firebase/auth";
    
    const provider = new SAMLAuthProvider("saml.myProvider");

    Web 版本 8

    const provider = new firebase.auth.SAMLAuthProvider('saml.myProvider');
  2. 启动登录流程。您可以选择使用弹出式窗口或重定向。

    Web 版本 9

    import { getAuth, signInWithPopup, SAMLAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    signInWithPopup(auth, provider)
      .then((result) => {
        // User is signed in.
        // Provider data available from the result.user.getIdToken()
        // or from result.user.providerData
      }).catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The AuthCredential type that was used.
        const credential = SAMLAuthProvider.credentialFromError(error);
        // Handle / display error.
        // ...
      });

    Web 版本 8

    firebase.auth().signInWithPopup(provider)
      .then((result) => {
        // User is signed in.
        // Identity provider data available in result.additionalUserInfo.profile,
        // or from the user's ID token obtained from result.user.getIdToken()
        // as an object in the firebase.sign_in_attributes custom claim
        // This is also available from result.user.getIdTokenResult()
        // idTokenResult.claims.firebase.sign_in_attributes.
      })
      .catch((error) => {
        // Handle / display error.
        // ...
      });

    重定向

    如需重定向到登录页面,请调用 signInWithRedirect()

    Web 版本 9

    import { getAuth, signInWithRedirect } from "firebase/auth";
    
    const auth = getAuth();
    signInWithRedirect(auth, provider);

    Web 版本 8

    firebase.auth().signInWithRedirect(provider);

    然后,调用 getRedirectResult() 以在用户返回您的应用时获取结果:

    Web 版本 9

    import { getAuth, getRedirectResult, SAMLAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    getRedirectResult(auth)
      .then((result) => {
        // User is signed in.
        // Provider data available from the result.user.getIdToken()
        // or from result.user.providerData
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The AuthCredential type that was used.
        const credential = SAMLAuthProvider.credentialFromError(error);
        // Handle / display error.
        // ...
      });

    Web 版本 8

    firebase.auth().getRedirectResult()
      .then((result) => {
        // User is signed in.
        // Provider data available in result.additionalUserInfo.profile,
        // or from the user's ID token obtained from result.user.getIdToken()
        // as an object in the firebase.sign_in_attributes custom claim
        // This is also available from result.user.getIdTokenResult()
        // idTokenResult.claims.firebase.sign_in_attributes.
      }).catch((error) => {
        // Handle / display error.
        // ...
      });
  3. 使用 firebase.sign_in_attributes 声明从 ID 令牌中检索与 SAML 提供商相关的用户特性。在将 ID 令牌 发送到服务器时,务必使用 Admin SDK 验证 ID 令牌。

    只有当用户的电子邮件地址在 ID 令牌中 身份提供方的 SAML 断言的 NameID 属性:

    <Subject>
      <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">test@email.com</NameID>
    </Subject>
    

    此值会填充在 Firebase 签发的 ID 令牌和 UserInfo 对象中。

目前仅支持服务提供商通过 Client SDK 启动的 SAML 流程。

关联用户账号

如果用户已使用其他方法(例如电子邮件/密码)登录到应用,您可以使用 linkWithPopup()linkWithRedirect() 将其现有账号关联到 SAML 提供商: 例如,我们可以使用 Google 账号进行关联:

Web 版本 9

import { getAuth, linkWithPopup, GoogleAuthProvider } from "firebase/auth";
const provider = new GoogleAuthProvider();

const auth = getAuth();
linkWithPopup(auth.currentUser, provider).then((result) => {
  // Accounts successfully linked.
  const credential = GoogleAuthProvider.credentialFromResult(result);
  const user = result.user;
  // ...
}).catch((error) => {
  // Handle Errors here.
  // ...
});

Web 版本 8

auth.currentUser.linkWithPopup(provider).then((result) => {
  // Accounts successfully linked.
  var credential = result.credential;
  var user = result.user;
  // ...
}).catch((error) => {
  // Handle Errors here.
  // ...
});

后续步骤