使用 OpenID Connect 进行身份验证(Apple 平台)

如果您已升级到 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 添加到您的 iOS 项目

  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 Apple 平台 SDK 处理登录流程,请按以下步骤操作:

  1. 将自定义网址方案 (URL scheme) 添加到 Xcode 项目中:

    1. 打开项目配置:在左侧的树状视图中双击项目名称。在目标部分中选择您的应用,然后选择信息标签页,并展开网址类型部分。
    2. 点击 + 按钮,然后将经过编码的应用 ID 添加为网址方案。您可以打开 Firebase 控制台的常规设置页面,在您的 iOS 应用部分找到经过编码的应用 ID。请将其他字段留空。

      完成上述操作后,您的配置应显示如下(但其中的值应替换为您的应用的值):

      Xcode 自定义网址方案设置界面的屏幕截图
  2. 使用您在 Firebase 控制台中获得的提供方 ID 创建 OAuthProvider 实例。

    Swift

    var provider = OAuthProvider(providerID: "oidc.example-provider")
    

    Objective-C

    FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:@"oidc.example-provider"];
    
  3. 可选:指定您希望通过 OAuth 请求发送的其他自定义 OAuth 参数。

    Swift

    provider.customParameters = [
      "login_hint": "user@example.com"
    ]
    

    Objective-C

    [provider setCustomParameters:@{@"login_hint": @"user@example.com"}];
    

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

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

    Swift

    provider.scopes = ["mail.read", "calendars.read"]
    

    Objective-C

    [provider setScopes:@[@"mail.read", @"calendars.read"]];
    

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

  5. 可选:如果您希望自定义应用在向用户显示 reCAPTCHA 时如何呈现 SFSafariViewControllerUIWebView,请创建一个符合 AuthUIDelegate 协议的自定义类。

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

    Swift

    // If you created a custom class that conforms to AuthUIDelegate,
    // pass it instead of nil:
    provider.getCredentialWith(nil) { credential, error in
      if error != nil {
        // Handle error.
      }
      if credential != nil {
        Auth().signIn(with: credential) { authResult, error in
          if error != nil {
            // Handle error.
          }
          // User is signed in.
          // IdP data available in authResult.additionalUserInfo.profile.
          // OAuth access token can also be retrieved:
          // (authResult.credential as? OAuthCredential)?.accessToken
          // OAuth ID token can also be retrieved:
          // (authResult.credential as? OAuthCredential)?.idToken
        }
      }
    }
    

    Objective-C

    // If you created a custom class that conforms to AuthUIDelegate,
    // pass it instead of nil:
    [provider getCredentialWithUIDelegate:nil
                                completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) {
      if (error) {
        // Handle error.
      }
      if (credential) {
        [[FIRAuth auth] signInWithCredential:credential
                                  completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
          if (error) {
            // Handle error.
          }
          // User is signed in.
          // IdP data available in authResult.additionalUserInfo.profile.
          // OAuth access token can also be retrieved:
          // ((FIROAuthCredential *)authResult.credential).accessToken
          // OAuth ID token can also be retrieved:
          // ((FIROAuthCredential *)authResult.credential).idToken
        }];
      }
    }];
    
  7. 以上示例侧重的是登录流程。除此之外,您也可以使用 linkWithCredential 将 OIDC 提供方与现有用户相关联。例如,您可以将多个提供方关联至同一个用户,以便使用任意一个进行登录。

    Swift

    Auth().currentUser.link(withCredential: credential) { authResult, error in
      if error != nil {
        // Handle error.
      }
      // OIDC credential is linked to the current user.
      // IdP data available in authResult.additionalUserInfo.profile.
      // OAuth access token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.accessToken
      // OAuth ID token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.idToken
    }
    

    Objective-C

    [[FIRAuth auth].currentUser
        linkWithCredential:credential
                completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      if (error) {
        // Handle error.
      }
      // OIDC credential is linked to the current user.
      // IdP data available in authResult.additionalUserInfo.profile.
      // OAuth access token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).accessToken
      // OAuth ID token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).idToken
    }];
    
  8. 上述模式同样适用于 reauthenticateWithCredential。对于要求用户必须在近期内登录过才能执行的敏感操作,可使用它来检索新的凭据。

    Swift

    Auth().currentUser.reauthenticateWithCredential(withCredential: credential) { authResult, error in
      if error != nil {
        // Handle error.
      }
      // User is re-authenticated with fresh tokens minted and
      // should be able to perform sensitive operations like account
      // deletion and email or password update.
      // IdP data available in result.additionalUserInfo.profile.
      // Additional OAuth access token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.accessToken
      // OAuth ID token can also be retrieved:
      // (authResult.credential as? OAuthCredential)?.idToken
    }
    

    Objective-C

    [[FIRAuth auth].currentUser
        reauthenticateWithCredential:credential
                          completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      if (error) {
        // Handle error.
      }
      // User is re-authenticated with fresh tokens minted and
      // should be able to perform sensitive operations like account
      // deletion and email or password update.
      // IdP data available in result.additionalUserInfo.profile.
      // Additional OAuth access token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).accessToken
      // OAuth ID token can also be retrieved:
      // ((FIROAuthCredential *)authResult.credential).idToken
    }];
    

手动处理登录流程

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

Swift

let credential = OAuthProvider.credential(
    withProviderID: "oidc.example-provider",  // As registered in Firebase console.
    idToken: idToken,  // ID token from OpenID Connect flow.
    rawNonce: nil
)
Auth.auth().signIn(with: credential) { authResult, error in
    if error {
        // Handle error.
        return
    }
    // User is signed in.
    // IdP data available in authResult?.additionalUserInfo?.profile
}

Objective-C

FIROAuthCredential *credential =
    [FIROAuthProvider credentialWithProviderID:@"oidc.example-provider"  // As registered in Firebase console.
                                       IDToken:idToken  // ID token from OpenID Connect flow.
                                      rawNonce:nil];
[[FIRAuth auth] signInWithCredential:credential
                          completion:^(FIRAuthDataResult * _Nullable authResult,
                                      NSError * _Nullable error) {
    if (error != nil) {
        // Handle error.
        return;
    }
    // User is signed in.
    // IdP data available in authResult.additionalUserInfo.profile
}];

后续步骤

在用户首次登录后,系统会创建一个新的用户账号,并将其与该用户登录时使用的凭据(即用户名和密码、电话号码或者身份验证提供方信息)相关联。此新账号存储在您的 Firebase 项目中,无论用户采用何种方式登录,您项目中的每个应用都可以使用此账号来识别用户。

  • 在您的应用中,您可以从 User 对象获取用户的基本个人资料信息。请参阅管理用户

  • 在您的 Firebase Realtime DatabaseCloud Storage 安全规则中,您可以从 auth 变量获取已登录用户的唯一用户 ID,然后利用此 ID 来控制用户可以访问哪些数据。

您可以通过将身份验证提供方凭据关联至现有用户账号,让用户可以使用多个身份验证提供方登录您的应用。

如需将用户退出登录,请调用 signOut:

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

您可能还需要为所有身份验证错误添加错误处理代码。请参阅处理错误