#안드로이드 Third party 로그인 연동 4(페이스북 연동)

2019. 9. 15. 20:46 개발 이야기/android

 

페이스북 로그인 연동을 알아볼 건데요!

 

먼저 페이스북 개발자 사이트에 패키지를 등록하고 

 

키 해시값을 넣어야만 인증 로직을 사용할 수 있습니다

아래와 같이 되어있는데요 키 해시값이 많은 이유는

각각 개발자 자리에서 얻은 해시값을 넣어주어야 각각 자리에서 빌드했을 때 페이스북 로그인을 이용할 수 

있기 때문입니다!

 

이렇게 해시를 등록하면 되는데요! 해시는 어떻게 구하느냐!

카카오 로그인에서 다루었던 해시 추출 함수를 이용해서 얻어낼 수 있습니다!

    public static String getKeyHash(final Context context) {
        Dlog.Companion.d("카카오톡 개발자 해시키 가져오는 함수");
        PackageInfo packageInfo = getPackageInfo(context, PackageManager.GET_SIGNATURES);
        if (packageInfo == null)
            return null;

        for (Signature signature : packageInfo.signatures) {
            try {
                MessageDigest md = MessageDigest.getInstance("SHA");
                md.update(signature.toByteArray());
                return android.util.Base64.encodeToString(md.digest(), android.util.Base64.NO_WRAP);
            } catch (NoSuchAlgorithmException e) {
                Log.w("112", "Unable to get MessageDigest. signature=" + signature, e);
            }
        }
        return null;
    }

이런 식으로 로그를 통해 찍어주어도 되고요 개발자 마음대로! 사용하시면 됩니다

 

 

먼저 로그인 함수를 보겠습니다!

    @Override
    public void Login(Context context) {

        mContext = context;
        redirect = RedirectActivity.getInstance();
        auth = FirebaseAuth.getInstance();
        callbackManager = CallbackManager.Factory.create();

        // 토큰이 유효한 상태에서는
        AccessToken accessToken = AccessToken.getCurrentAccessToken();
        boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
        if (isLoggedIn) {
            Dlog.Companion.d("페이스북 " + auth);
            Dlog.Companion.d("페이스북 세션 있음");

            handleFacebookAccessToken(accessToken, context);
        } else {
            LoginManager.getInstance().registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
                @Override
                public void onSuccess(LoginResult loginResult) {
                    Dlog.Companion.d("페이스북 로그인 성공1");
                    handleFacebookAccessToken(loginResult.getAccessToken(), context);
                }

                @Override
                public void onCancel() {
                    Dlog.Companion.d("취소됨");
                }

                @Override
                public void onError(FacebookException error) {
                    Dlog.Companion.d("에러");

                }
            });
        }
    }

AccessToken과 토큰의 expired를 통해서 로그인 상태를 알아낼 수 있는데요!

로그인 상태에 따라 callback에 던저주던지!

아니면 바로 handleFacebookAccessToken함수를 호출하면 됩니다

handleFacebookAccessToken 함수를 보겠습니다.

private static FirebaseAuth auth;


// 페이스북 로그인 이벤트
// 사용자가 정상적으로 로그인한 후 페이스북 로그인 버튼의 onSuccess 콜백 메소드에서 로그인한 사용자의
// 액세스 토큰을 가져와서 Firebase 사용자 인증 정보로 교환하고,
// Firebase 사용자 인증 정보를 사용해 Firebase에 인증.
    private void handleFacebookAccessToken(com.facebook.AccessToken token, Context context) {
        if (context instanceof LoginActivity) {
            ((LoginActivity) context).startProgress();
        }
        AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
        auth.signInWithCredential(credential)
                .addOnCompleteListener((Activity) mContext, task -> {
                    if (task.isComplete()) {
                        facebookRequest(context);
                        Dlog.Companion.d("페이스북 로그인 성공" + auth.getCurrentUser());
                    } else {
                        Dlog.Companion.d("페이스북 로그인 실패");
                    }
                });
    }

    public static void registCallbackManager(int requestCode, int resultCode, Intent data, Context context) {
        try {
            callbackManager.onActivityResult(requestCode, resultCode, data);
        } catch (Exception e) {
            Dlog.Companion.d("페이스북 : 인증 없이 종료함" + e);
            Toast.makeText(context, "페이스북 인증이 종료되었습니다.", Toast.LENGTH_SHORT).show();
        }
    }

 

여기서 조금 헷갈리실 수 있는 부분이 있는데요!

auth 변수는 firebaseAuth 객체인데요!

왜 이게 여기서도 사용되나? 궁금해하실 것 같아요!

물론 다르게 구현할 수도 있는 부분이지만!

 

저의 경우에는 하나만이라도 관리하기 편할 수 있도록

페이스북과 firebase를 또 연동해봤는데요!

이런 식으로 제공업체가 페이스북인 상태로 파이어베이스에 등록되게 됩니다!

 

자 그럼 이어서!

AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());

파이어 베이스 로그인을 하고 얻은 토큰을 이용해서 credential을 만들어주고! 

이 결과를 가지고

auth.signInWithCredential(credential)
                .addOnCompleteListener((Activity) mContext, task -> {
                    if (task.isComplete()) {
                        facebookRequest(context);
                        Dlog.Companion.d("페이스북 로그인 성공" + auth.getCurrentUser());
                    } else {
                        Dlog.Companion.d("페이스북 로그인 실패");
                    }
                });

 로그인을 시도하게 됩니다!

즉, 페이스북 로그인 결과로 파이어베이스에 로그인을 하는 건데요!

 

이런 구조이기 때문에 페이스북으로 로그인을 해도

파이어베이스의 인스턴스가 살아있는데요!

 

이점에 대해서도 상당히 까다로운 버그가 발생될 수가 있기 때문에 

 

꼭 자세히 읽어봐 주세요

 

다음은 로그아웃 부분입니다

    public static void signOut(Context context) {
        AccessToken accessToken = AccessToken.getCurrentAccessToken();
        boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
        if (isLoggedIn) {
            Dlog.Companion.d("페이스북 로그아웃");
            FirebaseAuth.getInstance().signOut();
            LoginManager.getInstance().logOut();
            RedirectActivity.getInstance().signOutAfterLoginActivity(context);
        } else {
            Dlog.Companion.d("페이스북 세션 없음");
        }

    }

로그아웃을 할 때 페이스북 로그아웃과 파이어베이스 로그아웃을 함께 실행해주는데요

 

이유는?

페이스북만 로그아웃을 하고 로그인 엑티비티로 넘어가면

 

미리 만들어놓은 파이어베이스의 인증 확인후 인증이 정상적이라면!

파이어베이스 객체가 살아있다면! 자동 로그인 시도를 하는 로직을 만들어놔서

계속 버그가 발생되기 때문입니다

 

물론 이 부분은 구현하기 나름이고, 기획하기 나름인데요

 

아마도 많은 개발자분들도 저와 생각이 크게 다르지 않을 것 같아서 저랑 같은 곤욕을 치르실 꺼라 예상해봅니다

 

 

마지막으로 탈퇴 부분인데요

파이어베이스 회원탈퇴에서 언급했듯이!

파이어베이스는 탈퇴 시 재 인증을 요구하는데요!

직접 비밀번호를 받지 않는 페이스북은 아래와 같이 재인증을 해줍니다.

    public static void Unlink(Context context) {
        Dlog.Companion.d("페이스북 언링크");
        FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
        AccessToken accessToken = AccessToken.getCurrentAccessToken();
        if (user != null) {
            boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
            Dlog.Companion.d("페이스북 계정 삭제" + user.getProviderId());
            if (isLoggedIn) {
                AuthCredential credential = FacebookAuthProvider.getCredential(accessToken.getToken());
                user.reauthenticate(credential)
                        .addOnSuccessListener(task -> {
                            Dlog.Companion.d("페이스북 계정 reauthenticate 성공");
                            user.unlink("facebook.com")
                                    .addOnSuccessListener(task1 -> {
                                        user.delete();
                                        Dlog.Companion.d("페이스북 계정 unlink 성공" + user.getProviderId());
                                        FirebaseAuth.getInstance().signOut();
                                        LoginManager.getInstance().logOut();
                                        requestModule.deleteAccount(context);
                                    });

                        });
            } else {
                RedirectActivity.getInstance().signOut(context);
            }
        } else {
            RedirectActivity.getInstance().signOut(context);
        }
    }

 

페이스북 토큰을 이용해 FacebookAuthProvider의 credential을 만들어 주고

  AuthCredential credential = FacebookAuthProvider.getCredential(accessToken.getToken());

 

이 credential로 재인증을 해줍니다

user.reauthenticate(credential)
                        .addOnSuccessListener(task -> {
                            Dlog.Companion.d("페이스북 계정 reauthenticate 성공");
                            user.unlink("facebook.com")
                                    .addOnSuccessListener(task1 -> {
                                        user.delete();
                                        Dlog.Companion.d("페이스북 계정 unlink 성공" + user.getProviderId());
                                        FirebaseAuth.getInstance().signOut();
                                        LoginManager.getInstance().logOut();
                                        requestModule.deleteAccount(context);
                                    });

                        });

그리고 나서 user 객체의 unlink를 해준 뒤 

 

로그아웃!!!! 을 꼭 다시 해줍니다

 

로그아웃을 꼭 다시 안 해줘도 상관없지만 저의 경우에는 

 

세션이 남아있으면 자동으로 재인증을 하는 로직을 구현해놨기 때문에

 

이런 식으로 로그아웃을 꼭 해줘야 했습니다

 

 

이상으로 4개의 Third party 로그인을 모두 구현해봤는데요

 

아직 개선할 점이 있을 수도 있기 때문에 참고해서 개발해주시고

 

저의 프로젝트의 컨셉과 다른 로직을 구현하시는 분들은 필요한 부분만 참고해주시면 될 것 같습니다