使用 OpenID Connect 进行身份验证(Web 应用)

如果您已升级到 Firebase Authentication with Identity Platform,则可以使用所选的 OpenID Connect (OIDC) 合规提供方对用户进行 Firebase 身份验证。这样,您就可以使用 Firebase 不提供原生支持的身份提供方。

准备工作

如需让用户使用 OIDC 提供方登录,您必须先从该提供方处收集一些信息:

  • 客户端 ID:提供方用于标识您的应用的唯一字符串。您的提供方可能会针对您支持的每个平台为您分配一个不同的客户端 ID。这是您的提供方发放的 ID 令牌中的 aud 声明值之一。

  • 客户端密钥:提供方用于确认客户端 ID 所有权的密钥字符串。对于每个客户端 ID,您都需要一个匹配的客户端密钥。(仅当您使用身份验证代码流程 [强烈建议使用] 时才需要此值。)

  • 发放者:用于标识您的提供方的字符串。此值必须是网址,在附加 /.well-known/openid-configuration 后表示提供方的 OIDC 发现文档的位置。例如,如果发放者为 https://auth.example.com,则发现文档必须在 https://auth.example.com/.well-known/openid-configuration 位置提供。

获得上述信息后,启用 OpenID Connect 作为 Firebase 项目的登录提供方:

  1. 将 Firebase 添加至您的 JavaScript 项目

  2. 如果您尚未升级到 Firebase Authentication with Identity Platform,请升级。OpenID Connect 身份验证仅适用于升级后的项目。

  3. Firebase 控制台的登录提供方页面上,点击添加新提供方,然后点击 OpenID Connect

  4. 选择是要使用授权代码流程还是隐式授权流程。

    如果提供方支持,您应始终使用代码流程。隐式流程的安全性较低,强烈建议不要使用。

  5. 为此提供方命名。请记下系统生成的提供方 ID(类似于 oidc.example-provider)。向应用添加登录代码时,您需要用到此 ID。

  6. 指定您的客户端 ID 和客户端密钥,以及提供方的发放者字符串。这些值必须与提供方分配给您的值完全一致。

  7. 保存更改。

使用 Firebase SDK 处理登录流程

如果您想要使用 OIDC 提供方通过 Firebase 验证用户身份,较简单的方法是使用 Firebase SDK 来处理整个登录流程。

如需使用 Firebase JavaScript SDK 处理登录流程,请按以下步骤操作:

  1. 使用您在 Firebase 控制台中获得的提供方 ID 创建 OAuthProvider 实例。

    Web

    import { OAuthProvider } from "firebase/auth";
    
    const provider = new OAuthProvider('oidc.example-provider');
    

    Web

    var provider = new firebase.auth.OAuthProvider('oidc.example-provider');
    
  2. 可选:指定您希望通过 OAuth 请求发送的其他自定义 OAuth 参数。

    Web

    provider.setCustomParameters({
      // Target specific email with login hint.
      login_hint: 'user@example.com'
    });
    

    Web

    provider.setCustomParameters({
      // Target specific email with login hint.
      login_hint: 'user@example.com'
    });
    

    请与您的提供方联系,了解其支持的参数。请注意,您不能使用 setCustomParameters 传递 Firebase 必需的参数。这些参数包括 client_idresponse_typeredirect_uristatescoperesponse_mode

  3. 可选:指定您希望向身份验证提供方申请获取的个人资料基本信息以外的额外 OAuth 2.0 范围。

    Web

    provider.addScope('mail.read');
    provider.addScope('calendars.read');
    

    Web

    provider.addScope('mail.read');
    provider.addScope('calendars.read');
    

    请与您的提供方联系,了解其支持的范围。

  4. 使用 OAuth 提供方对象进行 Firebase 身份验证。

    您可以将用户重定向到提供方的登录页面,也可以在弹出式浏览器窗口中打开登录页面。

    重定向流程

    调用 signInWithRedirect() 重定向到提供方登录页面:

    Web

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

    Web

    firebase.auth().signInWithRedirect(provider);
    

    用户完成登录并返回到您的应用后,您可以调用 getRedirectResult() 获取登录结果。

    Web

    import { getAuth, getRedirectResult, OAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    getRedirectResult(auth)
      .then((result) => {
        // User is signed in.
        // IdP data available in result.additionalUserInfo.profile.
    
        // Get the OAuth access token and ID Token
        const credential = OAuthProvider.credentialFromResult(result);
        const accessToken = credential.accessToken;
        const idToken = credential.idToken;
      })
      .catch((error) => {
        // Handle error.
      });
    

    Web

    firebase.auth().getRedirectResult()
      .then((result) => {
        // IdP data available in result.additionalUserInfo.profile.
        // ...
    
        /** @type {firebase.auth.OAuthCredential} */
        var credential = result.credential;
    
        // OAuth access and id tokens can also be retrieved:
        var accessToken = credential.accessToken;
        var idToken = credential.idToken;
      })
      .catch((error) => {
        // Handle error.
      });
    

    弹出式窗口流程

    Web

    import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    signInWithPopup(auth, provider)
      .then((result) => {
        // User is signed in.
        // IdP data available using getAdditionalUserInfo(result)
    
        // Get the OAuth access token and ID Token
        const credential = OAuthProvider.credentialFromResult(result);
        const accessToken = credential.accessToken;
        const idToken = credential.idToken;
      })
      .catch((error) => {
        // Handle error.
      });
    

    Web

    firebase.auth().signInWithPopup(provider)
      .then((result) => {
        // IdP data available in result.additionalUserInfo.profile.
        // ...
    
        /** @type {firebase.auth.OAuthCredential} */
        var credential = result.credential;
    
        // OAuth access and id tokens can also be retrieved:
        var accessToken = credential.accessToken;
        var idToken = credential.idToken;
      })
      .catch((error) => {
        // Handle error.
      });
    
  5. 以上示例侧重的是登录流程。除此之外,您也可以使用同一模式通过 linkWithRedirect()linkWithPopup() 将 OIDC 提供方与现有用户相关联,然后使用 reauthenticateWithRedirect()reauthenticateWithPopup()(可用于为要求用户在近期登录的敏感操作检索新的凭据)重新验证用户身份。

手动处理登录流程

如果您已在应用中实现了 OpenID Connect 登录流程,可以直接使用 ID 令牌通过 Firebase 进行身份验证:

Web

import { getAuth, signInWithCredential, OAuthProvider } from "firebase/auth";

const provider = new OAuthProvider("oidc.example-provider");
const credential = provider.credential({
    idToken: idToken,
});
signInWithCredential(getAuth(), credential)
    .then((result) => {
        // User is signed in.
        // IdP data available in result.additionalUserInfo.profile.

        // Get the OAuth access token and ID Token
        const credential = OAuthProvider.credentialFromResult(result);
        const accessToken = credential.accessToken;
        const idToken = credential.idToken;
    })
    .catch((error) => {
        // Handle error.
    });

Web

const provider = new OAuthProvider("oidc.example-provider");
const credential = provider.credential({
    idToken: idToken,
});
firebase.auth().signInWithCredential(credential)
    .then((result) => {
        // User is signed in.
        // IdP data available in result.additionalUserInfo.profile.

        // Get the OAuth access token and ID Token
        const credential = OAuthProvider.credentialFromResult(result);
        const accessToken = credential.accessToken;
        const idToken = credential.idToken;
    })
    .catch((error) => {
        // Handle error.
    });