#안드로이드 Third party 로그인 연동 3(파이어베이스 연동)

2019. 9. 15. 19:04 개발 이야기/android

 

오늘은 파이어베이스를 이용한 안드로이드 로그인 연동을 해볼게요!

 

파이어 베이스는 다른 로그인 연동과는 조금 다르지만 비슷하게 연동 과정이 필요한데요!

 

카카오, 네이버와 같은 경우는 각 개발 사이트에서 앱을 만들고 나눠준 해시값을 등록하는 부분이 필요한데

 

파이어베이스의 경우는 google-services.json 파일을 내려받아서 안드로이드 프로젝트 내에 넣어주어야 합니다!

 

이런 식으로 파이어베이스 콘솔에서 그림의 우측 하단에 보이는 google-services.json 다운로드 버튼을 눌러서

 

다운로드한 후! 

 

이런 식으로 프로젝트 디렉터리 내에 위치시켜주면 준비는 끝!

 

이제 Firebase 클래스를 만들어주고 다른 SNS 연동 클래스와 마찬가지로 CRUD를 만들어 줍니다!

네이버와 마찬가지고 토큰 관련해서 갱신이 필요한 부분이 있기 때문에

관련 버그 때문에 고생하시는 분들은 잘 확인해주세요!

 

우선 저는 회원가입 -> 검증 이메일 전송 -> 이메일 확인 유무에 따른 로그인

이런 로직으로 구현했습니다.

 

먼저 초기 개발 컨셉이 조금 바뀌는 바람에 

함수 명이 마치 로그인을 하는 함수 같지만 .. ㅋㅋ Firebase의 인스턴스를 만들어주는 함수입니다!

(로그인 부분을 오랜 시간 건드리지 않다 보니.. 블로그를 쓰다가 반성하게 되네요)

    public void Login(Context context) {
        mContext = context;
        auth = FirebaseAuth.getInstance();

    }

 

다음은 인스턴스를 생성해준 후에 실제 로그인 함수인데요!

   public void InputLoginData(Context context, EditText inputEmail, EditText inputPassword) {
        final String email = inputEmail.getText().toString();
        final String password = inputPassword.getText().toString();

        if (validationCheck(email, password)) {  
            try {
                /* authenticate user */
                auth.signInWithEmailAndPassword(email, password)  // 실제 이메일 페스워드로 로그인 시도하는 로직
                        .addOnCompleteListener((Activity) context, task -> {
                            if (!task.isSuccessful()) {
                                Dlog.Companion.d("실패이유 " + task.getException().getMessage());
                                try {
								..............................
                                } catch (Exception e) {
                                    Dlog.Companion.e("프로그레스 에러" + e);
                                }
                            } else {
                                if (isEmailVerified()) {  // 이메일 확인 로직
                                    Request(context);
                                } else {
                                    ((LoginActivity) mContext).stopProgress();
                                    auth.signOut();
                                    Toast.makeText(context, "인증메일을 확인해주세요.", Toast.LENGTH_SHORT).show();
                                }
                            }
                        });
            } catch (Exception e) {
                Dlog.Companion.d("에러 정보 :" + e);
            }
        } else {
		..............................
        }


    }

로그인을 시도하는 로직 중에 따로 만든 isEmailVerified 함수를 이용해서

전송된 검증 메일을 확인했는지 검증하는 부분을 추가해서

만약 검증이 안됬다면 로그아웃해버리는 로직을 추가했습니다.

 

이 부분을 넣지 않으면 상상하고 계신 버그가 발생돼요 

 

다음은 회원가입 함수입니다.

    private void signUp() {
        Dlog.Companion.d("");
        auth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(SignUpActivity.this, task -> {
                    Dlog.Companion.d("가입 성공 : " + task.isSuccessful());
                    if (!task.isSuccessful()) {
                        emailResult.setText("해당 메일 주소의 가입 정보가 존재합니다.");
                        Dlog.Companion.d("계정 생성 실패 : " + task.getException());
                        inputEmail.setFocusable(true);
                        inputEmail.setFocusableInTouchMode(true);
                        btnCount = 0;
                        stopProgress();
                    } else if (task.isSuccessful()) {
                        Dlog.Companion.d("계정 생성 성공. 검증용 이메일 전송");
                        sendVerificationEmail();
                    }
                });
    }

이렇게 createUserWithEmailAndPassword 함수를 이용해서 회원가입을 한 후에

성공적이라면 ? task.isSuccessful()

sendVerificationEmail()함수를 실행시킵니다!

 

이 부분도 마치 이메일을 검증하는 부분 같지만 개발 컨셉이 바뀌다 보니.. 이름 명명이 저런 식으로 쓰고 있네요 ㅜ

 

이메일 전송 로직

    // 이메일 검증 메일
    private void sendVerificationEmail() {
        user = auth.getCurrentUser();
        user.sendEmailVerification()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        // email sent
                        // after email is sent just logout the user and finish this activity

                        Toast.makeText(context, "인증메일이 전송되었습니다.\n    메일을 확인해주세요.", Toast.LENGTH_SHORT).show();
                        stopProgress();
                        FirebaseAuth.getInstance().signOut();
                        final Intent intent = new Intent(context, LoginActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                        context.startActivity(intent);
                        finish();
                    } else {
                        // email not sent, so display message and restart the activity or do whatever you wish to do
                        //restart this activity
                        Toast.makeText(SignUpActivity.this, "검증 메일 전송 실패했습니다.", Toast.LENGTH_SHORT).show();
                        inputEmail.setFocusable(true);
                        inputEmail.setFocusableInTouchMode(true);
                        stopProgress();
                        btnCount = 0;
                    }
                });
    }

sendEmailVerification 함수를 통해서 이메일을 보내줍니다!

 

여기서 깨알 팁을 하나 말씀드리면 

 

파이어베이스는 검증 메일이 전송되려면 이미! 

파이어베이스에 등록이 된 상태입니다.

 

무슨 말이냐면 createUserWithEmailAndPassword 함수를 실행시킨 user는 이미 등록이 된 상태이고

등록된 user한테만 ! 검증 메일을 보낼 수가 있습니다. 조금은 비합리적인 로직이긴 하지만

파이어베이스를 사용하면 어쩔 수가 없습니다 그래서

추가적으로 검증이 안됬으면 파이어베이스에 등록된 계정을 삭제하는 등의 행위가 필요합니다!

 

검증 메일을 확인했는지 검증하는 함수입니다!

    public boolean isEmailVerified() {
        FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
        if (user != null) {
            AccessToken accessToken = AccessToken.getCurrentAccessToken();
            boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
            if (!isLoggedIn) {
                Dlog.Companion.d("파이어베이스 유저");
                if (!user.isEmailVerified()) {
                    Dlog.Companion.d("이메일 확인 안됨");
                    return false;
                } else {
                    Dlog.Companion.d("이메일 확인 완료~");
                    return true;
                }
            }
        }
        return false;
    }

간단하게 user.isEmailVerified()함수로 확인을 할 수 있습니다!

 

 

다음은 로그아웃 함수입니다

    public static void signOut(final Context context) {
        Dlog.Companion.d("파이어 베이스 페이스북 동시 로그아웃됨(세션 동일)");
        auth = FirebaseAuth.getInstance();
        FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
        Dlog.Companion.d("파이어 베이스 세션: " + user);
        try {
            if (user != null) {
                Dlog.Companion.d("파이어베이스 로그아웃");
                auth.signOut();
                RedirectActivity.getInstance().signOutAfterLoginActivity(context);
            } else {
                Dlog.Companion.d("파이어베이스 세션 null");
            }
        } catch (Exception e) {
            Dlog.Companion.d("" + e);
        }
    }

인스턴스를 가져와서 signOut()함수를 실행시켜주고 

 

로그아웃과 동시에 필요한 행위의 함수를 실행해주시면 됩니다!

 

 

마지막으로 회원 탈퇴 로직인데요!

파이어베이스의 정책 상 탈퇴를 하려면 재인증이 필요합니다!

무조건은 아닌 로그인한 후 1시간 경과 시에 이런 부분이 필요한데요!

로그인을 1시간마다 하지 않기 때문에 재인증 로직을 추가했습니다.

먼저  alertDialog를 만들어서 탈퇴할 계정의 아이디를 띄어주고 

계정의 비밀번호를 입력하도록 했는데요!

입력된 비밀번호는 계정 정보와 파이어베이스 세션과 함께 reauthenticate 함수의 파라미터로 들어가게 됩니다!

    public static void Unlink(Context context) {
        Dlog.Companion.d("파이어 베이스 언링크");
        FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
        if (user != null) {
            Dlog.Companion.d("user = " + user.getEmail());
            final LinearLayout linear = (LinearLayout) View.inflate(context, R.layout.unlink_dialog, null);
            AlertDialog ad = new AlertDialog.Builder(context).create();
            ad.setView(linear);
            TextView unlink_email = linear.findViewById(R.id.unlinkemail);
            unlink_email.setText(user.getEmail());

            final TextView.OnClickListener onClickListener = v -> {
                switch (v.getId()) {
                    case R.id.btn_ok:
                        TextView addboxdialog = linear.findViewById(R.id.addboxdialog);
                        String pw = addboxdialog.getText().toString();
                        Dlog.Companion.d("pw" + pw);
                        reauthenticate(user.getEmail(), pw, context, user);

                        break;
                    case R.id.btn_cancel:
                        ad.dismiss();
                        break;
                }
            };

            TextView ok_btn = linear.findViewById(R.id.btn_ok);
            TextView cancel_btn = linear.findViewById(R.id.btn_cancel);
            ok_btn.setOnClickListener(onClickListener);
            cancel_btn.setOnClickListener(onClickListener);
            ad.show();

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

    }

 

reauthenticate 함수의 소스코드입니다

    static void reauthenticate(String email, String pw, Context context, FirebaseUser user) {
        AuthCredential credential = EmailAuthProvider.getCredential(email, pw);
        user.reauthenticate(credential)
                .addOnCompleteListener(task -> {
                   if (task.isSuccessful()){
                       user.delete()
                                    .addOnCompleteListener(tasks -> {
                                        if (tasks.isSuccessful()) {
                                            Dlog.Companion.d("파이어 베이스 계정 삭제");
                                            requestModule.deleteAccount(context);
                                        }
                                    });
                   }else if (task.getException() != null){
                       Dlog.Companion.d("에러에러");
                       Toast.makeText(context,"비밀번호가 틀렸습니다",Toast.LENGTH_SHORT).show();
                   }
                });
    }

여기서 중요한 부분 위에서 말씀드렸다시피

 

로그인 후 1시간 경과 후 탈퇴를 할 때는 재인증이 필요합니다!

만약 재인증을 안 할 경우에는 아래와 같은 익셉션이 발생하게 됩니다

com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException: This operation is sensitive and requires recent authentication. Log in again before retrying this request.

 

그렇기 때문에 입력받은 비밀번호를 이용해서 AuthCredential 객체를 만들어 줍니다.

AuthCredential credential = EmailAuthProvider.getCredential(email, pw);

그리고 재인증 함수(user.reauthenticate(credential))를 실행시켜준 후에 

계정 삭제 함수 user.delete()와 서버에 저장된 정보를 삭제하는 함수를 실행시켜주시면 됩니다

 

여기까지가 파이어베이스 연동하는 방법이었는데요!

개발하다가 보니 필요한 부분들을 전달해드리고 싶어서 정리를 하고 있는데

 

잘 정리가 된 건지는 모르겠네요

 

원래는 페이스북을 먼저 작성하려고 했지만

 

페이스북도 파이어베이스와 연동해서 쓰고 있기 때문에

 

 

파이어베이스를 먼저 작성해봤습니다

 

참고 부탁드립니다 :)