Android에서 이메일 링크를 사용하여 Firebase에 인증하기

Firebase 인증을 사용하면 링크를 포함하는 이메일을 사용자에게 전송하여 로그인하는 것이 가능합니다. 이메일의 링크를 클릭하여 로그인할 수 있으며 이 과정에서 사용자의 이메일 주소도 인증됩니다.

이메일로 로그인하는 경우 다음과 같은 많은 이점이 있습니다.

  • 편리한 가입 및 로그인
  • 여러 애플리케이션에서 비밀번호를 재사용할 위험이 적음: 비밀번호를 재사용하면 안전하게 만든 비밀번호의 보안도 약화될 수 있습니다.
  • 사용자를 인증하는 동시에 사용자가 이메일 주소의 합법적인 소유자인지 확인하는 기능
  • 액세스 가능한 이메일 계정만 있으면 로그인 가능: 전화번호 또는 소셜 미디어 계정을 소유하지 않아도 됩니다.
  • 사용자가 모바일 기기에서 번거롭게 비밀번호를 입력하거나 기억할 필요 없이 안전하게 로그인 가능
  • 이전에 이메일 식별자(비밀번호 또는 제휴)로 로그인한 기존 사용자는 이메일만 사용하여 로그인하도록 업그레이드 가능: 예를 들어 사용자가 비밀번호를 기억하지 못하더라도 비밀번호를 재설정하지 않고 계속 로그인할 수 있습니다.

시작하기 전에

Android 스튜디오 프로젝트 설정

  1. Android 프로젝트에 Firebase를 추가합니다.
  2. 앱 수준 build.gradle 파일에 Firebase 인증 및 Google Play 서비스에 대한 종속 항목을 추가합니다.
    implementation 'com.google.firebase:firebase-auth:16.1.0'
    implementation 'com.google.android.gms:play-services-auth:16.0.1'
    

이메일 링크로 사용자를 로그인 처리하려면 우선 Firebase 프로젝트에서 이메일 제공업체 및 이메일 링크 로그인 방법을 사용 설정해야 합니다.

  1. Firebase 콘솔에서 인증 섹션을 엽니다.
  2. 로그인 방법 탭에서 이메일/비밀번호 제공업체를 사용 설정합니다. 이메일 링크 로그인을 사용하려면 이메일/비밀번호 로그인이 사용 설정되어야 합니다.
  3. 같은 섹션에서 이메일 링크(비밀번호가 없는 로그인) 로그인 방법을 사용 설정합니다.
  4. 저장을 클릭합니다.

이 인증 과정을 시작하려면 사용자에게 이메일 주소를 제공하도록 요청하는 인터페이스를 제시하고 sendSignInLinkToEmail을 호출하여 Firebase가 사용자의 이메일에 인증 링크를 전송하도록 요청합니다.

  1. Firebase에 이메일 링크를 만드는 방법에 대한 지침을 제시하는 ActionCodeSettings 객체를 만듭니다. 다음 필드를 설정합니다.

    • url: 삽입할 딥 링크 및 함께 전달할 추가 상태. 승인된 도메인의 Firebase 콘솔 목록에서 링크의 도메인을 허용 목록에 추가해야 합니다. 이는 로그인 방법 탭(승인 -> 로그인 방법)에 있습니다. 사용자 기기에 앱이 설치되어 있지 않고 앱을 설치할 수 없는 경우에 인증 링크는 사용자를 이 URL로 리디렉션합니다.
    • androidPackageNameIOSBundleId: Android 또는 iOS 기기에서 로그인 링크를 열 때 사용하는 앱입니다. 모바일 앱을 통해 이메일 작업 링크를 열기 위해 Firebase 동적 링크를 구성하는 방법에 대해 자세히 알아보세요.
    • handleCodeInApp: true로 설정합니다. 다른 대역 외 이메일 작업(비밀번호 재설정 및 이메일 확인)과 달리 이 로그인 작업은 항상 앱에서 완료해야 합니다. 그 이유는 인증 과정의 끝에서 사용자가 로그인하고 사용자의 인증 상태를 앱에서 유지해야 하기 때문입니다.
    • dynamicLinkDomain: 프로젝트에 대해 여러 개의 맞춤 동적 링크 도메인이 정의된 경우 지정된 모바일 앱을 통해 링크를 열 때 사용할 도메인을 지정합니다(예: example.page.link). 그렇지 않으면 첫 번째 도메인이 자동으로 선택됩니다.

    자바
    Android

    ActionCodeSettings actionCodeSettings =
            ActionCodeSettings.newBuilder()
                    // URL you want to redirect back to. The domain (www.example.com) for this
                    // URL must be whitelisted in the Firebase Console.
                    .setUrl("https://www.example.com/finishSignUp?cartId=1234")
                    // This must be true
                    .setHandleCodeInApp(true)
                    .setIOSBundleId("com.example.ios")
                    .setAndroidPackageName(
                            "com.example.android",
                            true, /* installIfNotAvailable */
                            "12"    /* minimumVersion */)
                    .build();

    Kotlin
    Android

    val actionCodeSettings = ActionCodeSettings.newBuilder()
            // URL you want to redirect back to. The domain (www.example.com) for this
            // URL must be whitelisted in the Firebase Console.
            .setUrl("https://www.example.com/finishSignUp?cartId=1234")
            // This must be true
            .setHandleCodeInApp(true)
            .setIOSBundleId("com.example.ios")
            .setAndroidPackageName(
                    "com.example.android",
                    true, /* installIfNotAvailable */
                    "12" /* minimumVersion */)
            .build()

    ActionCodeSettings에 대해 자세히 알아보려면 이메일 작업의 상태 전달 섹션을 참조하세요.

  2. 사용자에게 이메일을 묻습니다.

  3. 사용자의 이메일에 인증 링크를 전송하고 사용자가 같은 기기에서 이메일 로그인을 완료할 경우를 대비해 사용자의 이메일을 저장합니다.

    자바
    Android

    FirebaseAuth auth = FirebaseAuth.getInstance();
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Email sent.");
                    }
                }
            });

    Kotlin
    Android

    val auth = FirebaseAuth.getInstance()
    auth.sendSignInLinkToEmail(email, actionCodeSettings)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Email sent.")
                }
            }

보안 문제

로그인 링크를 의도하지 않은 사용자 또는 기기로 로그인하는 데 사용하는 것을 방지하기 위해 Firebase 인증에서는 로그인 과정을 완료할 때 사용자의 이메일 주소를 입력해야 합니다. 로그인하려면 이 이메일 주소가 처음에 로그인 링크를 보낸 주소와 일치해야 합니다.

링크를 요청한 같은 기기에서 로그인 링크를 여는 사용자를 위해 이 과정을 간소화할 수 있습니다. 예를 들어 로그인 이메일을 보낼 때 SharedPreferences를 사용하여 사용자의 이메일 주소를 로컬에 저장하면 됩니다. 그런 다음 이 이메일 주소를 사용하여 이 과정을 완료합니다. 세션 주입이 활성화될 수 있으므로 사용자의 이메일을 리디렉션 URL 매개변수에서 전달해서는 안 되며 재사용해서도 안 됩니다.

로그인이 완료되면 확인되지 않은 이전 로그인 메커니즘은 사용자에게서 모두 삭제되고 기존 세션은 무효화됩니다. 예를 들어 누군가가 이전에 같은 이메일과 비밀번호로 확인되지 않은 계정을 만든 경우 이 사용자의 비밀번호는 삭제됩니다. 명의를 도용해 확인되지 않은 계정을 만들었던 사람이 이 이메일과 비밀번호로 다시 로그인하는 것을 방지하기 위해서입니다.

또한 중개 서버에서 링크를 가로채지 않도록 프로덕션 시 HTTPS URL을 사용해야 합니다.

Android 앱에서 로그인 완료

Firebase 인증에서는 Firebase 동적 링크를 사용하여 모바일 기기로 이메일 링크를 보냅니다. 모바일 애플리케이션을 통해 로그인을 완료하는 경우 애플리케이션에서 수신 애플리케이션 링크를 감지하고 기본 딥 링크를 파싱한 다음 로그인을 완료하도록 구성해야 합니다.

Firebase 인증은 모바일 애플리케이션에서 열릴 링크를 보낼 때 Firebase 동적 링크를 사용합니다. 이 기능을 사용하려면 Firebase 콘솔에서 동적 링크를 구성해야 합니다.

  1. Firebase 동적 링크를 사용 설정합니다.

    1. Firebase 콘솔에서 동적 링크 섹션을 엽니다.
    2. 동적 링크 약관에 아직 동의하지 않았으며 동적 링크 도메인도 만들지 않았다면 지금 동의하고 만듭니다.

      동적 링크 도메인을 이미 만들었다면 기록해 둡니다. 동적 링크 도메인의 형식은 일반적으로 다음 예와 같습니다.

      example.page.link

      수신 링크를 가로채도록 Android 또는 iOS 앱을 구성할 때 이 값이 필요합니다.

  2. Android 애플리케이션 구성:

    1. Android 애플리케이션에서 이 링크를 처리하려면 Firebase 콘솔 프로젝트 설정에서 Android 패키지 이름을 지정해야 합니다. 또한 애플리케이션 인증서의 SHA-1 및 SHA-256을 제공해야 합니다.
    2. 이제 동적 링크 도메인을 추가하고 Android 앱이 올바르게 구성되었으므로 동적 링크가 런처 활동에서 시작하여 애플리케이션으로 리디렉션됩니다.
    3. 동적 링크를 특정 활동으로 리디렉션하려면 AndroidManifest.xml 파일에 인텐트 필터를 구성해야 합니다. 인텐트 필터에서 동적 링크 도메인 또는 이메일 작업 핸들러를 지정하여 이 작업을 수행할 수 있습니다. 기본적으로 이메일 작업 핸들러는 다음 예와 같이 도메인에 호스팅됩니다.
      PROJECT_ID.firebaseapp.com/
    4. 주의사항:
      1. 인텐트 필터에서 actionCodeSettings에 설정한 URL을 지정하지 마세요.
      2. 동적 링크 도메인을 만들 때 단축 URL 링크를 만들었더라도 이 단축 URL은 전달되지 않습니다. android:pathPrefix 속성으로 이 단축 URL을 포착하도록 인텐트 필터를 구성하지 마세요. 즉, 애플리케이션의 다른 부분에서 다른 동적 링크를 포착할 수 없습니다. 하지만 수행하려는 작업을 알아보거나 isSignInWithEmailLink와 같은 SDK 메소드를 사용하여 앱에서 수신한 링크가 원하는 작업을 수행하는지 알아보기 위해 링크에서 mode 쿼리 매개변수를 확인할 수 있습니다.
    5. 동적 링크 수신에 대한 자세한 내용은 Android 동적 링크 수신 안내를 참조하세요.

위에 설명된 대로 링크를 수신하면 이메일 링크 인증을 위한 링크인지 확인하고 로그인을 완료합니다.

자바
Android

FirebaseAuth auth = FirebaseAuth.getInstance();
Intent intent = getIntent();
String emailLink = intent.getData().toString();

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    String email = "someemail@domain.com";

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "Successfully signed in with email link!");
                        AuthResult result = task.getResult();
                        // You can access the new user via result.getUser()
                        // Additional user info profile *not* available via:
                        // result.getAdditionalUserInfo().getProfile() == null
                        // You can check if the user is new or existing:
                        // result.getAdditionalUserInfo().isNewUser()
                    } else {
                        Log.e(TAG, "Error signing in with email link", task.getException());
                    }
                }
            });
}

Kotlin
Android

val auth = FirebaseAuth.getInstance()
val intent = intent
val emailLink = intent.data!!.toString()

// Confirm the link is a sign-in with email link.
if (auth.isSignInWithEmailLink(emailLink)) {
    // Retrieve this from wherever you stored it
    val email = "someemail@domain.com"

    // The client SDK will parse the code from the link for you.
    auth.signInWithEmailLink(email, emailLink)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Successfully signed in with email link!")
                    val result = task.result
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error signing in with email link", task.exception)
                }
            }
}

iOS 애플리케이션에서 이메일 링크를 사용한 로그인 처리 방법을 알아보려면 iOS 가이드를 참조하세요.

웹 애플리케이션에서 이메일 링크를 사용한 로그인 처리 방법을 알아보려면 웹 가이드를 참조하세요.

기존 사용자에게도 이 인증 방법을 연결할 수 있습니다. 예를 들어 전화번호 등 다른 제공업체로 전에 인증된 사용자의 경우 기존 사용자 계정에 이 로그인 방법을 추가할 수 있습니다.

이 경우 작업 뒷부분이 달라집니다.

자바
Android

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Link the credential to the current user.
auth.getCurrentUser().linkWithCredential(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Successfully linked emailLink credential!");
                    AuthResult result = task.getResult();
                    // You can access the new user via result.getUser()
                    // Additional user info profile *not* available via:
                    // result.getAdditionalUserInfo().getProfile() == null
                    // You can check if the user is new or existing:
                    // result.getAdditionalUserInfo().isNewUser()
                } else {
                    Log.e(TAG, "Error linking emailLink credential", task.getException());
                }
            }
        });

Kotlin
Android

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Link the credential to the current user.
auth.currentUser!!.linkWithCredential(credential)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                Log.d(TAG, "Successfully linked emailLink credential!")
                val result = task.result
                // You can access the new user via result.getUser()
                // Additional user info profile *not* available via:
                // result.getAdditionalUserInfo().getProfile() == null
                // You can check if the user is new or existing:
                // result.getAdditionalUserInfo().isNewUser()
            } else {
                Log.e(TAG, "Error linking emailLink credential", task.exception)
            }
        }

민감한 작업을 실행하기 전에 이메일 링크 사용자를 재인증하는 경우에도 사용할 수 있습니다.

자바
Android

// Construct the email link credential from the current URL.
AuthCredential credential =
        EmailAuthProvider.getCredentialWithLink(email, emailLink);

// Re-authenticate the user with this credential.
auth.getCurrentUser().reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // User is now successfully reauthenticated
                } else {
                    Log.e(TAG, "Error reauthenticating", task.getException());
                }
            }
        });

Kotlin
Android

// Construct the email link credential from the current URL.
val credential = EmailAuthProvider.getCredentialWithLink(email, emailLink)

// Re-authenticate the user with this credential.
auth.currentUser!!.reauthenticateAndRetrieveData(credential)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // User is now successfully reauthenticated
            } else {
                Log.e(TAG, "Error reauthenticating", task.exception)
            }
        }

하지만 원래의 사용자가 로그인하지 않은 다른 기기에서 인증 과정이 종료되면 이 인증 과정이 완료되지 않을 수 있습니다. 이러한 경우 같은 기기에서 링크를 열도록 사용자에게 오류를 표시할 수 있습니다. 링크에 일부 상태를 전달하여 작업 유형 및 사용자 uid에 대한 정보를 표시할 수 있습니다.

이메일/비밀번호 로그인과 이메일 링크 기반 로그인을 모두 지원하는 경우 비밀번호 또는 링크 사용자에 맞춰 로그인 방법을 구별하려면 fetchSignInMethodsForEmail을 사용합니다. 이렇게 하면 사용자에게 먼저 이메일 입력을 요청한 다음 로그인 방법을 제시하는 다음과 같은 식별자 우선 인증 과정에 유용합니다.

자바
Android

auth.fetchSignInMethodsForEmail(email)
        .addOnCompleteListener(new OnCompleteListener<SignInMethodQueryResult>() {
            @Override
            public void onComplete(@NonNull Task<SignInMethodQueryResult> task) {
                if (task.isSuccessful()) {
                    SignInMethodQueryResult result = task.getResult();
                    List<String> signInMethods = result.getSignInMethods();
                    if (signInMethods.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)) {
                        // User can sign in with email/password
                    } else if (signInMethods.contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
                        // User can sign in with email/link
                    }
                } else {
                    Log.e(TAG, "Error getting sign in methods for user", task.getException());
                }
            }
        });

Kotlin
Android

auth.fetchSignInMethodsForEmail(email)
        .addOnSuccessListener { result ->
            val signInMethods = result.signInMethods
            if (signInMethods!!.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)) {
                // User can sign in with email/password
            } else if (signInMethods.contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
                // User can sign in with email/link
            }
        }
        .addOnFailureListener { exception ->
            Log.e(TAG, "Error getting sign in methods for user", exception)
        }

위에 설명된 대로 이메일/비밀번호 및 이메일/링크는 다른 로그인 방법을 사용하지만 같은 EmailAuthProvider(같은 PROVIDER_ID)로 간주됩니다.

다음 단계

사용자가 처음으로 로그인하면 신규 사용자 계정이 생성되고 사용자 인증 정보(사용자가 로그인할 때 사용한 사용자 이름과 비밀번호, 전화번호 또는 인증 제공업체 정보)에 연결됩니다. 이 신규 계정은 Firebase 프로젝트에 저장되며 사용자의 로그인 방법과 무관하게 프로젝트 내의 모든 앱에서 사용자 본인 확인에 사용할 수 있습니다.

  • 앱에서 FirebaseUser 객체로부터 사용자의 기본 프로필 정보를 가져올 수 있습니다. 사용자 관리를 참조하세요.

  • Firebase 실시간 데이터베이스와 Cloud Storage 보안 규칙에서 auth 변수로부터 로그인한 사용자의 고유 사용자 ID를 가져온 후 이 ID를 통해 사용자가 액세스할 수 있는 데이터를 관리할 수 있습니다.

인증 제공업체의 사용자 인증 정보를 기존 사용자 계정에 연결하면 사용자가 여러 인증 제공업체를 통해 앱에 로그인할 수 있습니다.

사용자를 로그아웃시키려면 signOut을 호출합니다.

자바
Android

FirebaseAuth.getInstance().signOut();

Kotlin
Android

FirebaseAuth.getInstance().signOut()

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.