bestsource

처음에 Facebook을 사용한 인증 후 Google이 Android용 Firebase에서 오류를 발생시킵니다.

bestsource 2023. 6. 8. 21:14
반응형

처음에 Facebook을 사용한 인증 후 Google이 Android용 Firebase에서 오류를 발생시킵니다.

Firebase Docs에서 이해한 바와 같이, 사용자가 자격 증명으로 계정을 인증한 경우, 자격 증명이 아직 다른 자격 증명과 연결되지 않은 경우 동일한 자격 증명을 사용하여 엄격하게 로그인해야 합니다.

즉, Google 로그인을 사용하여 계정을 만든 다음 (로그아웃 후) Google 인증 정보에 사용되는 것과 동일한 전자 메일을 사용하여 Facebook 인증 정보로 로그인하려고 하면 logcat에 다음 예외가 표시됩니다.

"전자 메일 주소는 같지만 로그인 자격 증명이 다른 계정이 이미 있습니다.이 전자 메일 주소와 연결된 공급자를 사용하여 로그인하십시오."

그리고 네, 저는 당연히 이 예외를 받습니다.그러나 Facebook을 사용하여 계정을 만든 다음 Google 자격 증명으로 로그인하려고 하면 이 계정(Facebook)의 공급자가 Google로 전환됩니다.이번 인증은 실패하지 않지만 예상한 결과는 아닙니다.어떤 식으로든 각 사용자를 특정 자격 증명과 연결하고 싶습니다.이걸 어떻게 고쳐야 하나요?아래 코드를 볼 수 있습니다.

public class SignInActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener,
        View.OnClickListener {

    private static final String TAG = "SignInActivity";
    private static final int RC_SIGN_IN = 9001;

    private GoogleApiClient mGoogleApiClient;
    private FirebaseAuth mFirebaseAuth;
    private FirebaseAuth.AuthStateListener mFirebaseAuthListener;

    private CallbackManager mCallbackManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_sign_in);

        // Facebook Login
        FacebookSdk.sdkInitialize(getApplicationContext());
        mCallbackManager = CallbackManager.Factory.create();

        LoginButton mFacebookSignInButton = (LoginButton) findViewById(R.id.facebook_login_button);
        mFacebookSignInButton.setReadPermissions("email", "public_profile");

        mFacebookSignInButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                Log.d(TAG, "facebook:onSuccess:" + loginResult);
                firebaseAuthWithFacebook(loginResult.getAccessToken());
            }

            @Override
            public void onCancel() {
                Log.d(TAG, "facebook:onCancel");
            }

            @Override
            public void onError(FacebookException error) {
                Log.d(TAG, "facebook:onError", error);
            }
        });

        // Google Sign-In
        // Assign fields
        SignInButton mGoogleSignInButton = (SignInButton) findViewById(R.id.google_sign_in_button);

        // Set click listeners
        mGoogleSignInButton.setOnClickListener(this);

        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id))
                .requestEmail()
                .build();
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
                .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .build();

        // Initialize FirebaseAuth
        mFirebaseAuth = FirebaseAuth.getInstance();

        mFirebaseAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in
                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                    // User is signed out
                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
            }
        };
    }

    @Override
    public void onStart() {
        super.onStart();
        mFirebaseAuth.addAuthStateListener(mFirebaseAuthListener);
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mFirebaseAuthListener != null) {
            mFirebaseAuth.removeAuthStateListener(mFirebaseAuthListener);
        }
    }

    private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
        Log.d(TAG, "firebaseAuthWithGooogle:" + acct.getId());
        AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
        mFirebaseAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds
                        // the auth state listener will be notified and logic to handle the
                        // signed in user can be handled in the listener.
                        if (!task.isSuccessful()) {
                            Log.w(TAG, "signInWithCredential", task.getException());
                            Toast.makeText(SignInActivity.this, "Authentication failed.",
                                    Toast.LENGTH_SHORT).show();
                        } else {
                            startActivity(new Intent(SignInActivity.this, MainActivity.class));
                            finish();
                        }
                    }
                });
    }

    private void firebaseAuthWithFacebook(AccessToken token) {
        Log.d(TAG, "handleFacebookAccessToken:" + token);

        final AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
        mFirebaseAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds
                        // the auth state listener will be notified and logic to handle the
                        // signed in user can be handled in the listener.
                        if (!task.isSuccessful()) {
                            Log.w(TAG, "signInWithCredential", task.getException());
                            Toast.makeText(SignInActivity.this, "Authentication failed.",
                                    Toast.LENGTH_SHORT).show();
                        }

                        else {
                            startActivity(new Intent(SignInActivity.this, MainActivity.class));
                            finish();
                        }
                    }
                });
    }

    /*
    private void handleFirebaseAuthResult(AuthResult authResult) {
        if (authResult != null) {
            // Welcome the user
            FirebaseUser user = authResult.getUser();
            Toast.makeText(this, "Welcome " + user.getEmail(), Toast.LENGTH_SHORT).show();

            // Go back to the main activity
            startActivity(new Intent(this, MainActivity.class));
        }
    }
    */

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.google_sign_in_button:
                signIn();
                break;
            default:
                return;
        }
    }

    private void signIn() {
        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        mCallbackManager.onActivityResult(requestCode, resultCode, data);

        // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
        if (requestCode == RC_SIGN_IN) {
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            if (result.isSuccess()) {
                // Google Sign In was successful, authenticate with Firebase
                GoogleSignInAccount account = result.getSignInAccount();
                firebaseAuthWithGoogle(account);
            } else {
                // Google Sign In failed
                Log.e(TAG, "Google Sign In failed.");
            }
        }
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        // An unresolvable error has occurred and Google APIs (including Sign-In) will not
        // be available.
        Log.d(TAG, "onConnectionFailed:" + connectionResult);
        Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
    }
}

인증 > 로그인 공급자로 이동하여 전자 메일 주소당 여러 계정을 클릭하고 동일한 전자 메일 주소로 여러 계정을 만들 수 있도록 허용을 클릭합니다.

계정 전자 메일 주소 설정

스레드 https://groups.google.com/forum/ #!searchin/firebase-talk/liu/firebase-talk/ms_NVQem_Cw/8g7BFk1IAAAJ를 확인하십시오. 이러한 현상이 발생하는 이유를 설명합니다.이는 Google 전자 메일이 확인되지 않는 반면 Facebook 전자 메일은 확인되지 않는 일부 보안 문제 때문입니다.

저는 마침내 다음과 같은 논리로 끝을 맺었습니다.

사용자가 Facebook으로 로그인하려고 하지만 지정된 전자 메일을 가진 사용자가 이미 존재하고(Google 공급자 사용) 다음 오류가 발생하는 경우:

"전자 메일 주소는 같지만 로그인 자격 증명이 다른 계정이 이미 있습니다.이 전자 메일 주소와 연결된 공급자를 사용하여 로그인하십시오."

따라서 사용자에게 Google을 사용하여 로그인하도록 요청하십시오(그리고 Facebook을 기존 계정에 자동으로 연결한 후).

Firebase를 사용한 Facebook 및 Google Sign Logics

Firebase에서는 사용자가 Facebook에 처음 로그인할 때 확인 이메일을 보내 사용자 이메일 계정을 확인하는 것이 매우 중요합니다.

이메일이 확인되면 사용자가 이메일 주소로 using @gmail.com 인 경우 Facebook과 Gmail로 모두 로그인할 수 있습니다.

Facebook 로그인 -> 확인 이메일에서 링크 클릭 -> Gmail 로그인 -> Facebook 로그인 (OK)

Facebook 로그인 -> Gmail 로그인 -> 확인 이메일에서 링크 클릭 -> Facebook 로그인 (NOT OK)

사용자가 로그아웃하기 전에 Facebook 이메일을 확인하지 않고 Gmail로 로그인하려고 하면 Gmail로 로그인하는 순간 Facebook으로 다시 로그인할 수 없습니다.

업데이트 - Facebook 전자 메일을 항상 신뢰하도록 선택한 경우.

Facebook 계정을 통해 처음 로그인할 때 emailVerified를 true로 자동 설정하는 Firebase 기능(트리거)을 설정할 수 있습니다.

샘플 코드.

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.app = functions.auth.user().onCreate( async (user) => {

  if (user.providerData.find(d => d && d.providerId === 'facebook.com') || user.providerData === 'facebook.com') {
    
      try {
      await admin.auth().updateUser(user.uid, {
          emailVerified: true
        })
      } catch (err) {
        console.log('err when verifying email', err)
      }
  }
})

문서: Firebase 인증 트리거

계정 보안을 손상시키지 않고 로그인 UI 클릭을 최소화하기 위해 Firebase Authentication에는 ID 공급자가 전자 메일 서비스 공급자이기도 한 '신뢰할 수 있는 공급자' 개념이 있습니다.예를 들어 Google은 신뢰할 수 있는 공급자 for @gmail.com 주소이고 Yahoo는 신뢰할 수 있는 공급자 for @yahoo.com 주소이며 Microsoft for @outlook.com 주소입니다.

"전자 메일 주소당 하나의 계정" 모드에서 Firebase 인증은 전자 메일 주소를 기준으로 계정을 연결하려고 합니다.사용자가 신뢰할 수 있는 공급자에서 로그인하면 사용자가 전자 메일 주소를 소유하고 있음을 알고 있으므로 사용자는 즉시 계정에 로그인합니다.

전자 메일 주소는 같지만 신뢰할 수 없는 자격 증명(예: 신뢰할 수 없는 공급자 또는 암호)으로 생성된 기존 계정이 있으면 보안상의 이유로 이전 자격 증명이 제거됩니다.전자 메일 주소 소유자가 아닌 피싱자가 초기 계정을 만들 수 있습니다. 초기 자격 증명을 제거하면 피싱자가 나중에 계정에 액세스할 수 없게 됩니다.

진류

Somebody Special이 작성한 대로 Facebook 사용자는 이메일 및 암호(암호 =)뿐만 아니라 이메일 확인도 필요합니다.

나는 사용자가 Googe 이메일을 사용하면 자동으로 확인된 상태가 된다고 생각합니다.

Google이 해결해야 할 문제:

사용자가 Facebook으로 로그인한 후 Gmail로 로그인하면 이메일 확인을 완료하지 않은 경우 계정이 작성됩니다.나중에 이메일을 확인해도 변경 사항이 없습니다. 계정을 덮어씁니다.

사용자가 Facebook과 인증 이메일(gmail)로 로그인하면 모든 것이 괜찮다고 생각합니다. 두 소셜 미디어 제공자를 모두 사용하여 로그인할 수 있습니다.

동일한 전자 메일 주소로 여러 계정을 만들 수 있도록 허용하는 것이 좋습니다.

백엔드에서 전자 메일을 확인하고 사용자에 대한 참조인 경우에만 작동합니다.Firebase ID를 사용하면 고유 사용자를 유지할 수 없습니다.

저도 같은 문제가 있었습니다.그래서 나는 구글 계정과 페이스북 계정을 삭제합니다.

파이어베이스 콘솔 > 인증 > 사용자

서로 테스트하기 전에 로그인한 적이 있습니다.

저도 같은 문제가 있었습니다. Firebase Console로 이동한 다음 "Authentication" 범주에서 원하는 사용자를 삭제하기만 하면 됩니다.

그것은 저에게 효과가 있습니다.

언급URL : https://stackoverflow.com/questions/37947944/authentication-using-facebook-at-first-and-then-google-causes-an-error-in-fireba

반응형