关联多个身份验证提供方
您可以通过将多个身份验证提供方凭据关联至现有用户账号,让用户可以使用多个身份验证提供方登录您的应用。无论用户使用哪个身份验证提供方登录,系统均可通过同一 Firebase 用户 ID 识别用户。例如,使用密码登录的用户可以关联 Google 账号,以后便可使用这两种方法中的任意一种登录。或者,匿名用户可以关联 Facebook 账号,以后就可以使用 Facebook 账号登录并继续使用您的应用。
准备工作
为您的应用增加对两个或多个身份验证提供方(可能包括匿名身份验证)的支持。
将身份验证提供方凭据与用户账号关联
如需将身份验证提供方凭据与现有用户账号关联,请按以下步骤操作:
- 使用任意身份验证提供方或方法让用户登录。
- 按照新身份验证提供方的登录流程逐步进行,直到需要调用某一
FIRAuth.signInWith方法时停止。例如:获取用户的 Google ID 令牌、Facebook 访问令牌或电子邮件地址和密码。 - 为此新身份验证提供方获取
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 数据。
取消身份验证提供方与用户账号的关联
一个 Firebase 用户账号可以关联多个身份验证提供方(例如邮箱/密码、Google、Facebook),这样用户就可以通过不同的方法登录同一个 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) {
// ...
}];
问题排查
如果您在尝试关联多个账号时遇到错误,请参阅有关已验证邮箱的文档。
