web-dev-qa-db-ja.com

Appleでのサインインに認証情報を再利用する

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!)

                        }
                    }
                }
            }
        }
    }
_
2
kAiN

だから、これは私がいつも使ってきた解決策であり、Firebaseのドキュメント自体に記載されています。まず、Multiple Auth Providersのリンクの概念を理解する必要があります。多くの皆さんがこの問題に直面している限り、私はできる限り精巧になるよう努めています。 FirebaseGoogleが所有しており、Googleでサインインするたびに、Apple Sign InFacebookなどの他のサインインメソッドが自動的にオーバーライドされます、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
}

これで、リンクのアラートが表示されます。まあ、どのように進めたいかはあなた次第です。私のアプリでは、GoogleFacebook、およびEmailサインインメソッドを有効にしており、2つのプロバイダーがFirebase。たとえば.

GoogleFacebookですでにサインアップしている場合、ユーザーにメールでサインアップしようとすると、2つのアラートボタンLink With GoogleLink With Facebookが表示されます。両方を表示するか、1つだけを表示するかは、あなた次第です。ここにApple Sign Inを追加することもできます。個人の選択次第です。

ここで、アラートが表示され、Link With Facebookオプションを最初に押すと、togglelinkAccount変数が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 

        }
    })

}

今、これが私が試した解決策であり、それはいつも私のために働いた。 SwiftFlutterJavascriptで動作し、他のすべての言語で動作します。 Apple Sign In Buttonでも同じことができます。ユーザーがメールアドレスを共有した場合に詳細を取得するデリゲートでは、fetchSignInMethodsを使用でき、上記のアラートで自動的に表示されます。 IDを秘密にしておくことを選択したユーザーをリンクする機会はありませんが、すぐに回答を更新します。

できる限り詳しく説明するよう努めましたが、ご不明な点がありましたらお知らせください。

Once Email, Facebook and Google are linked

0
Rob