Firebase Documentation 同じメールで異なる認証情報を使用してアカウントエラーを管理する方法を読んだ後、Firebaseのドキュメントで推奨されているようにコードを変更しましたが、まだ問題が見つかります
Facebookでの認証で_AuthErrorCode.accountExistsWithDifferentCredential.rawValue
_エラーが発生すると、一時的な認証情報が取得されます(_(error! As NSError) .userInfo [AuthErrorUserInfoUpdatedCredentialKey]
_で発生する可能性があります)
次に、Firebase内でAuth.auth().fetchSignInMethods
を使用して、ユーザーがこれまでに使用したプロバイダーを検索します
それが(私の例では)プロバイダー "Apple.com"を満たしている場合、ドキュメントはauthenticateユーザーと取得した一時的な認証情報を使用して、プロバイダーとのリンクを(すべてうまくいく場合)作成します。
今私の問題は、Auth.auth()。fetchSignInMethodsがプロバイダー "Apple.com"Apple資格情報は1度しか使用できないため、このプロバイダーで認証できません...なので、この時点で、この状況からどうやって抜け出すのでしょうか?
ドキュメントに従うと、EmailAuthProviderIDで認証するように言われますが、明らかにこれは単なる例だと思います...ドキュメントでは、私は会ったのではなく、 (私の場合と同様)プロバイダー "Apple.com"に適合EmailAuthProviderID...
このドキュメントの解釈に誤りはありますか?
私は他の間違いをして、それを実現していませんか?
Facebookアカウントを既存のAppleアカウントにリンクできませんか?
申し訳ありませんが、私は何日もこの問題に頭を悩ませてきました。
これは更新されたコードです
_Auth.auth().signIn(with: FacebookAuthProvider.credential(withAccessToken: authToken)) { authResult, error in
if error != nil {
// Handle error.
if (error as NSError?)?.code == AuthErrorCode.accountExistsWithDifferentCredential.rawValue {
// Get pending credential and email of existing account.
let existingAcctEmail = (error! as NSError).userInfo[AuthErrorUserInfoEmailKey] as! String
let pendingCred = (error! as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as! AuthCredential
Auth.auth().fetchSignInMethods(forEmail: existingAcctEmail) { (methods, error) in
if (methods?.contains("Apple.com"))! {
// **** This Flow stops here because I can't reuse Apple's credentials a second time. *****
let tokenID = KeychainManager.getItemFromKeychain(forKey: AuthKey.applTokenID, keyPrefix: AuthKey.prefix)!
let nonce = KeychainManager.getItemFromKeychain(forKey: AuthKey.applNonce, keyPrefix: AuthKey.prefix)
let appleCredentials = OAuthProvider.credential(withProviderID: "Apple.com", accessToken: tokenID)
Auth.auth().signIn(with: appleCredentials) { user, error in
if user != nil {
// Link pending credential to account.
Auth.auth().currentUser?.link(with: pendingCred) { result, error in
// ...
print("\n LINK")
}
}
else {
print(error!)
}
}
}
}
}
}
_
だから、これは私がいつも使ってきた解決策であり、Firebase
のドキュメント自体に記載されています。まず、Multiple Auth Providers
のリンクの概念を理解する必要があります。多くの皆さんがこの問題に直面している限り、私はできる限り精巧になるよう努めています。 Firebase
はGoogle
が所有しており、Google
でサインインするたびに、Apple Sign In
、Facebook
などの他のサインインメソッドが自動的にオーバーライドされます、Google
。
したがって、それを解決するには、Google Sign In
ボタンを使用します。私はJavascript
でも同じことをしたので、JS
開発者がこれをフォローできる場合でも、私の回答を一般的にしています。
email
からGoogle
を取得し、FB, Apple
などのすべてのプロバイダーを取得する必要があります。次に、このメソッドを使用する必要があります:fetchSignInMethods
。
func fetchSignInMethods(provider: String, email: String) {
Auth.auth().fetchSignInMethods(forEmail: email) { (providers, error) in
if let error = error {
print(error)
return
}
let matched = providers?.filter{ $0 == provider }.count //Here I check if the provider I sent example `Facebook` and the fetched provider are same or not, if it is matched then I can go ahead and login the user directly else I have to show them linking alert as already this email exists with different sign in methods
if providers?.isEmpty || matched == 1 {
var credential: AuthCredential!
switch provider {
case Constants.LoginProviders.Google.rawValue: //google.com
credential = GoogleAuthProvider.credential(withIDToken: idToken,accessToken: token) //idToken and AccessToken are fetched from Google Sign In Button
default: //facebook.com
credential = FacebookAuthProvider.credential(withAccessToken: token) //AccessToken is fetched from Facebook Sign In Button
break
}
self.loginWithCredential(credentail: credential, provider: provider)
} //I am also checking if provider is empty then I can sign them up for Email and Password users using Auth.auth().createUserWithEmailAndPassword
else {
self.displayLinkingAlert(provider: fetchedProviderName)
}
}
}
//This function is used to display the alert when an account needs to be linked
//Provider is the parameter which will be fetched from LoginProviders(Constants.Swift)
private func displayLinkingAlert(provider: String) {
MKProgress.hide()
let providerName = getProviderName(provider: provider)
let alertC: UIAlertController = UIAlertController(title: "Link Accounts?", message: "This account is already linked with \(providerName). Do you want to link accounts?", preferredStyle: .alert)
let linkAction: UIAlertAction = UIAlertAction(title: "Link account with \(providerName)", style: .default) { (_) in
self.linkAccounts.toggle() //Toggle linkAccounts to true to link the accounts of user
switch provider {
case Constants.LoginProviders.Google.rawValue:
GIDSignIn.sharedInstance()?.signIn()
break
default: self.facebookLoginWithPermissions(from: LoginViewController())
}
}
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let actions: [UIAlertAction] = [linkAction, cancelAction]
actions.forEach{ alertC.addAction($0) }
UIApplication.topViewController()?.present(alertC, animated: true, completion: nil)
} //I also too a private var linkAccounts = false above in the class
//This function is used to get the providerName from the provider
//Ex.:- provider -> google.com, providerName: Google
private func getProviderName(provider: String) -> String
{
var providerName = ""
switch provider
{
case Constants.LoginProviders.Email.rawValue : providerName = "Email"
case Constants.LoginProviders.Facebook.rawValue : providerName = "Facebook"
case Constants.LoginProviders.Google.rawValue : providerName = "Google"
default: break
}
return providerName
}
これで、リンクのアラートが表示されます。まあ、どのように進めたいかはあなた次第です。私のアプリでは、Google
、Facebook
、およびEmail
サインインメソッドを有効にしており、2つのプロバイダーがFirebase
。たとえば.
Google
とFacebook
ですでにサインアップしている場合、ユーザーにメールでサインアップしようとすると、2つのアラートボタンLink With Google
とLink With Facebook
が表示されます。両方を表示するか、1つだけを表示するかは、あなた次第です。ここにApple Sign In
を追加することもできます。個人の選択次第です。
ここで、アラートが表示され、Link With Facebook
オプションを最初に押すと、toggle
linkAccount
変数がtrueになり、次にfacebookTap
メソッドが呼び出されます。再びfetch
the providers
は次のようになります:
private func facebookTap() {
//Get the details and get the email then...
fetchSignInMethods(provider: Constants.LoginProviders.Facebook.rawValue //facebook.com, email: emailFetched) //This will sign in the user using Facebook and as the linkAccount variable is true it will link ther user
}
//A common function used to login the users, by using their credentails
//Parameter credential is of type AuthCredential which is used to
//signInAndRetrieve data of a particular user.
func loginWithCredential(credentail: AuthCredential, provider: String) {
authInstance.signInAndRetrieveData(with: credentail) { [unowned self](authResult, error) in
if let error = error {
print(error)
} else {
if self.linkAccounts {
var loginCredential: AuthCredential!
switch provider {
case Constants.LoginProviders.Google.rawValue:
guard let facebookTokenString = AccessToken.current?.tokenString else { return }
loginCredential = FacebookAuthProvider.credential(withAccessToken: facebookTokenString)
break
default:
loginCredential = GoogleAuthProvider.credential(withIDToken: self.googleIdToken, accessToken: self.googleAccessToken)
break
}
self.linkAccounts(credential: loginCredential)
} else {
self.getFirebaseToken()
}
}
}
}
今、私はこれをlink
アカウントに使用しています:
//This function is used to link the Accounts
//Ex:- Google with Facebook, vice-versa
private func linkAccounts(credential: AuthCredential) {
authInstance.currentUser?.link(with: credential, completion: { (authResult, error) in
if let error = error {
print(error)
return
} else {
self.linkAccounts.toggle() //Toggle Link Account to false as acccounts are already linked
//Navigate user to another page or do your stuff
}
})
}
今、これが私が試した解決策であり、それはいつも私のために働いた。 Swift
、Flutter
、Javascript
で動作し、他のすべての言語で動作します。 Apple Sign In Button
でも同じことができます。ユーザーがメールアドレスを共有した場合に詳細を取得するデリゲートでは、fetchSignInMethods
を使用でき、上記のアラートで自動的に表示されます。 IDを秘密にしておくことを選択したユーザーをリンクする機会はありませんが、すぐに回答を更新します。
できる限り詳しく説明するよう努めましたが、ご不明な点がありましたらお知らせください。