在 Apple 平台上将多个身份验证提供方与一个账号相关联

您可以通过将多个身份验证提供方凭据关联至现有用户账号,让用户可以使用多个身份验证提供方登录您的应用。无论用户使用哪个身份验证提供方登录,系统均可通过同一 Firebase 用户 ID 识别用户。例如,使用密码登录的用户可以关联 Google 账号,以后便可使用这两种方法中的任意一种登录。或者,匿名用户可以关联 Facebook 账号,以后就可以使用 Facebook 账号登录并继续使用您的应用。

准备工作

为您的应用增加对两个或多个身份验证提供方(可能包括匿名身份验证)的支持。

如需将身份验证提供方凭据与现有用户账号关联,请按以下步骤操作:

  1. 使用任意身份验证提供方或方法让用户登录。
  2. 按照新身份验证提供方的登录流程逐步进行,直到需要调用某一 FIRAuth.signInWith 方法时停止。例如:获取用户的 Google ID 令牌、Facebook 访问令牌或电子邮件地址和密码。
  3. 为此新身份验证提供方获取 FIRAuthCredential

    Google 登录
    Swift
    guard   let authentication = user?.authentication,   let idToken = authentication.idToken else {   return }  let credential = GoogleAuthProvider.credential(withIDToken: idToken,                                                accessToken: authentication.accessToken)
    Objective-C
    FIRAuthCredential *credential = [FIRGoogleAuthProvider credentialWithIDToken:result.user.idToken.tokenString                                  accessToken:result.user.accessToken.tokenString];
    Facebook 登录
    Swift
    let credential = FacebookAuthProvider   .credential(withAccessToken: AccessToken.current!.tokenString)
    Objective-C
    FIRAuthCredential *credential = [FIRFacebookAuthProvider     credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString];
    电子邮件地址/密码登录
    Swift
    let credential = EmailAuthProvider.credential(withEmail: email, password: password)
    Objective-C
    FIRAuthCredential *credential =     [FIREmailAuthProvider credentialWithEmail:email                                              password:password];
  4. FIRAuthCredential 对象传递给已登录用户的 linkWithCredential:completion: 方法:

    Swift
        user.link(with: credential) { authResult, error in   // ... } }
    Objective-C
        [[FIRAuth auth].currentUser linkWithCredential:credential     completion:^(FIRAuthDataResult *result, NSError *_Nullable error) {   // ... }];

    如果凭据已与另一用户账号关联,则 linkWithCredential:completion: 调用将会失败。在这种情况下,您必须根据需要为您的应用合并账号和相关数据:

    Swift

    let prevUser = Auth.auth().currentUser Auth.auth().signIn(with: credential) { authResult, error in     if let error = error {       let authError = error as NSError       if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue {         // The user is a multi-factor user. Second factor challenge is required.         let resolver = authError           .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver         var displayNameString = ""         for tmpFactorInfo in resolver.hints {           displayNameString += tmpFactorInfo.displayName ?? ""           displayNameString += " "         }         self.showTextInputPrompt(           withMessage: "Select factor to sign in\n\(displayNameString)",           completionBlock: { userPressedOK, displayName in             var selectedHint: PhoneMultiFactorInfo?             for tmpFactorInfo in resolver.hints {               if displayName == tmpFactorInfo.displayName {                 selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo               }             }             PhoneAuthProvider.provider()               .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil,                                  multiFactorSession: resolver                                    .session) { verificationID, error in                 if error != nil {                   print(                     "Multi factor start sign in failed. Error: \(error.debugDescription)"                   )                 } else {                   self.showTextInputPrompt(                     withMessage: "Verification code for \(selectedHint?.displayName ?? "")",                     completionBlock: { userPressedOK, verificationCode in                       let credential: PhoneAuthCredential? = PhoneAuthProvider.provider()                         .credential(withVerificationID: verificationID!,                                     verificationCode: verificationCode!)                       let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator                         .assertion(with: credential!)                       resolver.resolveSignIn(with: assertion!) { authResult, error in                         if error != nil {                           print(                             "Multi factor finanlize sign in failed. Error: \(error.debugDescription)"                           )                         } else {                           self.navigationController?.popViewController(animated: true)                         }                       }                     }                   )                 }               }           }         )       } else {         self.showMessagePrompt(error.localizedDescription)         return       }       // ...       return     }     // User is signed in     // ... }             // Merge prevUser and currentUser accounts and data             // ...         }

    Objective-C

    FIRUser *prevUser = [FIRAuth auth].currentUser; [[FIRAuth auth] signInWithCredential:credential                           completion:^(FIRAuthDataResult * _Nullable authResult,                                        NSError * _Nullable error) {     if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) {       FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];       NSMutableString *displayNameString = [NSMutableString string];       for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {         [displayNameString appendString:tmpFactorInfo.displayName];         [displayNameString appendString:@" "];       }       [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString]                            completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) {        FIRPhoneMultiFactorInfo* selectedHint;        for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {          if ([displayName isEqualToString:tmpFactorInfo.displayName]) {            selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo;          }        }        [FIRPhoneAuthProvider.provider         verifyPhoneNumberWithMultiFactorInfo:selectedHint         UIDelegate:nil         multiFactorSession:resolver.session         completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {           if (error) {             [self showMessagePrompt:error.localizedDescription];           } else {             [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName]                                  completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) {              FIRPhoneAuthCredential *credential =                  [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID                                                               verificationCode:verificationCode];              FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];              [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {                if (error) {                  [self showMessagePrompt:error.localizedDescription];                } else {                  NSLog(@"Multi factor finanlize sign in succeeded.");                }              }];            }];           }         }];      }];     }   else if (error) {     // ...     return;   }   // User successfully signed in. Get user data from the FIRUser object   if (authResult == nil) { return; }   FIRUser *user = authResult.user;   // ... }];                                     // Merge prevUser and currentUser accounts and data                                     // ...                                 }];

如果对 linkWithCredential:completion: 的调用成功,用户现在便可使用关联的任意身份验证提供方服务进行登录,并访问相同的 Firebase 数据。

您可以取消身份验证提供方与账号的关联,这样用户就无法再通过该提供方登录了。

如需取消身份验证提供方与用户账号的关联,请将提供方 ID 传递给 unlink 方法。您可以从 providerData 属性中获取与用户相关联的身份验证提供方的 ID。

Swift

Auth.auth().currentUser?.unlink(fromProvider: providerID!) { user, error in   // ... }

Objective-C

[[FIRAuth auth].currentUser unlinkFromProvider:providerID                                     completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {   // ... }];

问题排查

如果您在尝试关联多个账号时遇到错误,请参阅有关已验证邮箱的文档