Apple 플랫폼에서 이메일 링크를 사용하여 Firebase에 인증

Firebase 인증을 사용하면 로그인 링크를 이메일로 전송해서 사용자가 바로 로그인하게 할 수 있습니다. 이 과정에서 사용자의 이메일 주소도 확인됩니다.

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

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

시작하기 전에

Swift Package Manager를 사용해 Firebase 종속 항목을 설치하고 관리하세요.

  1. 앱 프로젝트를 연 상태로 Xcode에서 File(파일) > Add Packages(패키지 추가)로 이동합니다.
  2. 메시지가 표시되면 Firebase Apple 플랫폼 SDK 저장소를 추가합니다.
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. Firebase Authentication 라이브러리를 선택합니다.
  5. 타겟 빌드 설정의 Other Linker Flags(기타 링커 플래그) 섹션에 -ObjC 플래그를 추가합니다.
  6. 완료되면 Xcode가 백그라운드에서 자동으로 종속 항목을 확인하고 다운로드하기 시작합니다.

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

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

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

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

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

    Swift

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.url = URL(string: "https://www.example.com")
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
    actionCodeSettings.setAndroidPackageName("com.example.android",
                                             installIfNotAvailable: false, minimumVersion: "12")

    Objective-C

    FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
    [actionCodeSettings setURL:[NSURL URLWithString:@"https://www.example.com"]];
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = YES;
    [actionCodeSettings setIOSBundleID:[[NSBundle mainBundle] bundleIdentifier]];
    [actionCodeSettings setAndroidPackageName:@"com.example.android"
                        installIfNotAvailable:NO
                               minimumVersion:@"12"];

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

  2. 사용자에게 이메일 주소 입력을 요청합니다.

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

    Swift

    Auth.auth().sendSignInLink(toEmail: email,
                               actionCodeSettings: actionCodeSettings) { error in
      // ...
        if let error = error {
          self.showMessagePrompt(error.localizedDescription)
          return
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        UserDefaults.standard.set(email, forKey: "Email")
        self.showMessagePrompt("Check your email for link")
        // ...
    }

    Objective-C

    [[FIRAuth auth] sendSignInLinkToEmail:email
                       actionCodeSettings:actionCodeSettings
                               completion:^(NSError *_Nullable error) {
      // ...
        if (error) {
          [self showMessagePrompt:error.localizedDescription];
           return;
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        [NSUserDefaults.standardUserDefaults setObject:email forKey:@"Email"];
        [self showMessagePrompt:@"Check your email for link"];
        // ...
    }];

보안 문제

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

링크를 요청한 기기에서 로그인 링크를 여는 사용자를 위해 이 과정을 간소화할 수 있습니다. 로그인 이메일을 보낼 때 사용자의 이메일 주소를 로컬에 저장하면 됩니다. 그런 다음 이 이메일 주소를 사용하여 이 과정을 완료합니다.

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

Apple 모바일 앱에서 로그인 완료

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

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

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

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

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

      example.page.link

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

  2. Apple 애플리케이션을 구성합니다.

    1. 애플리케이션에서 이 링크를 처리하려면 Firebase Console 프로젝트 설정에서 번들 ID를 지정해야 합니다. App Store ID와 Apple Developer Team ID도 지정해야 합니다.
    2. 또한 애플리케이션 기능에서 이메일 작업 핸들러 도메인을 연결된 도메인으로 구성해야 합니다. 기본적으로 이메일 작업 핸들러는 다음 예시와 같이 도메인에 호스팅됩니다.
      APP_ID.firebaseapp.com
    3. iOS 버전 8 이하에 애플리케이션을 배포하려면 번들 ID를 수신 URL의 커스텀 스킴으로 설정해야 합니다.
    4. 자세한 내용은 Apple 플랫폼 동적 링크 수신 안내를 참조하세요.

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

Swift

if Auth.auth().isSignIn(withEmailLink: link) {
        Auth.auth().signIn(withEmail: email, link: self.link) { user, error in
          // ...
        }
}

Objective-C

if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
    [[FIRAuth auth] signInWithEmail:email
                               link:link
                         completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      // ...
    }];
}

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

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

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

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

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.link(with: credential) { authData, error in
    if (error) {
      // And error occurred during linking.
      return
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthProvider credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkWithCredential:credential
              completion:^(FIRAuthDataResult *_Nullable result,
                           NSError *_Nullable error) {
    if (error) {
      // And error occurred during linking.
      return;
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }];

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

Swift

  let credential = EmailAuthProvider.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticate(with: credential) { authData, error in
    if (error) {
      // And error occurred during re-authentication.
      return
    }
    // The user was successfully re-authenticated.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      reauthenticateWithCredential:credential
                        completion:^(FIRAuthDataResult *_Nullable result,
                                     NSError *_Nullable error) {
    if (error) {
      // And error occurred during re-authentication
      return;
    }
    // The user was successfully re-authenticated.
  }];

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

2023년 9월 15일 이후에 프로젝트를 만든 경우 이메일 열거 보호가 기본적으로 사용 설정됩니다. 이 기능은 프로젝트의 사용자 계정 보안을 개선하지만, 이전에 식별자 우선 인증 과정을 구현하는 데 권장되었던 fetchSignInMethodsForEmail() 메서드는 사용 중지됩니다.

프로젝트에서 이메일 열거 보호를 사용 중지할 수 있지만 그렇게 하지 않는 것이 좋습니다.

자세한 내용은 이메일 열거 보호에 대한 문서를 참조하세요.

다음 단계

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

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

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

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

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

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

또한 모든 인증 오류에 대한 오류 처리 코드를 추가할 수도 있습니다. 오류 처리를 참조하세요.