Google은 흑인 공동체를 위한 인종적 평등을 추구하기 위해 노력하고 있습니다. 자세히 알아보기

JavaScript에서 이메일 링크를 사용하여 Firebase로 인증

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

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

이메일로 로그인하면 다음과 같은 다양한 이점이 있습니다.

  • 마찰이 적은 가입 및 로그인.
  • 잘 선택된 비밀번호라도 보안을 약화시킬 수 있는 애플리케이션 전반에서 비밀번호 재사용의 위험을 낮춥니다.
  • 사용자를 인증하는 동시에 사용자가 이메일 주소의 합법적인 소유자인지 확인하는 기능.
  • 사용자는 액세스 가능한 이메일 계정만 있으면 로그인할 수 있습니다. 전화번호나 소셜 미디어 계정에 대한 소유권은 필요하지 않습니다.
  • 사용자는 모바일 장치에서 번거로울 수 있는 암호를 제공(또는 기억)할 필요 없이 안전하게 로그인할 수 있습니다.
  • 이전에 이메일 식별자(비밀번호 또는 연합)로 로그인한 기존 사용자는 이메일만으로 로그인하도록 업그레이드할 수 있습니다. 예를 들어 비밀번호를 잊어버린 사용자는 비밀번호를 재설정하지 않고도 로그인할 수 있습니다.

시작하기 전에

아직 복사하지 않았다면 JavaScript 프로젝트에 Firebase 추가 에 설명된 대로 Firebase 콘솔 에서 프로젝트로 초기화 스니펫을 복사합니다.

이메일 링크로 사용자를 로그인하려면 먼저 Firebase 프로젝트에 대한 이메일 제공업체 및 이메일 링크 로그인 방법을 활성화해야 합니다.

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

인증 흐름을 시작하려면 사용자에게 이메일 주소를 제공하라는 메시지를 표시하는 인터페이스를 제공한 다음 sendSignInLinkToEmail 을 호출하여 Firebase에서 사용자의 이메일로 인증 링크를 보내도록 요청합니다.

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

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

      Web version 9

      const actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

      Web version 8

      var actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

    ActionCodeSettings에 대한 자세한 내용 은 이메일 작업 섹션의 통과 상태를 참조하십시오.

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

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

    Web version 9

    import { getAuth, sendSignInLinkToEmail } from "firebase/auth";
    
    const auth = getAuth();
    sendSignInLinkToEmail(auth, email, actionCodeSettings)
      .then(() => {
        // 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.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        const errorCode = error.code;
        const errorMessage = error.message;
        // ...
      });

    Web version 8

    firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
      .then(() => {
        // 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.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        var errorCode = error.code;
        var errorMessage = error.message;
        // ...
      });

보안 문제

로그인 링크가 의도하지 않은 사용자로 로그인하거나 의도하지 않은 기기에서 사용되는 것을 방지하기 위해 Firebase 인증은 로그인 과정을 완료할 때 사용자의 이메일 주소를 제공하도록 요구합니다. 로그인에 성공하려면 이 이메일 주소가 로그인 링크가 원래 전송된 주소와 일치해야 합니다.

로그인 이메일을 보낼 때 localStorage 또는 쿠키를 사용하여 이메일 주소를 로컬에 저장하여 링크를 요청한 동일한 장치에서 로그인 링크를 여는 사용자를 위해 이 흐름을 간소화할 수 있습니다. 그런 다음 이 주소를 사용하여 흐름을 완료합니다. 리디렉션 URL 매개변수에 사용자의 이메일을 전달하지 말고 세션 삽입을 활성화할 수 있으므로 다시 사용하십시오.

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

또한 중개 서버가 링크를 가로챌 가능성을 방지하려면 프로덕션 환경에서 HTTPS URL을 사용해야 합니다.

웹 페이지에서 로그인 완료

이메일 링크 딥 링크의 형식은 대역 외 이메일 작업(이메일 확인, 비밀번호 재설정 및 이메일 변경 취소)에 사용되는 형식 과 동일합니다. Firebase 인증은 링크가 이메일 링크를 사용한 로그인인지 여부를 확인하는 isSignInWithEmailLink API를 제공하여 이 확인을 단순화합니다.

방문 페이지에서 로그인을 완료하려면 사용자의 이메일과 일회성 코드가 포함된 실제 이메일 링크를 사용하여 signInWithEmailLink 를 호출합니다.

Web version 9

import { getAuth, isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth";

// Confirm the link is a sign-in with email link.
const auth = getAuth();
if (isSignInWithEmailLink(auth, window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  let email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  signInWithEmailLink(auth, email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Web version 8

// Confirm the link is a sign-in with email link.
if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  var email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  firebase.auth().signInWithEmailLink(email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

모바일 앱에서 로그인 완료

Firebase 인증은 Firebase 동적 링크를 사용하여 이메일 링크를 모바일 장치로 보냅니다. 모바일 애플리케이션을 통한 로그인 완료의 경우 수신되는 애플리케이션 링크를 감지하고 기본 딥 링크를 구문 분석한 다음 웹 흐름을 통해 수행되는 대로 로그인을 완료하도록 애플리케이션을 구성해야 합니다.

Android 애플리케이션에서 이메일 링크로 로그인을 처리하는 방법에 대한 자세한 내용은 Android 가이드 를 참조하십시오.

Apple 애플리케이션에서 이메일 링크로 로그인을 처리하는 방법에 대한 자세한 내용은 Apple 플랫폼 가이드 를 참조하십시오.

이 인증 방법을 기존 사용자에 연결할 수도 있습니다. 예를 들어 전화번호와 같이 이전에 다른 공급자에게 인증된 사용자는 이 로그인 방법을 기존 계정에 추가할 수 있습니다.

차이점은 작업의 후반부에 있을 것입니다.

Web version 9

import { getAuth, linkWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
const auth = getAuth();
linkWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web version 8

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
firebase.auth().currentUser.linkWithCredential(credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

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

Web version 9

import { getAuth, reauthenticateWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
const auth = getAuth();
reauthenticateWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web version 8

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
firebase.auth().currentUser.reauthenticateWithCredential(credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

그러나 원래 사용자가 로그인하지 않은 다른 장치에서 흐름이 끝날 수 있으므로 이 흐름이 완료되지 않을 수 있습니다. 이 경우 동일한 장치에서 링크를 열도록 강제하는 오류가 사용자에게 표시될 수 있습니다. 작업 유형 및 사용자 uid에 대한 정보를 제공하기 위해 링크에서 일부 상태를 전달할 수 있습니다.

이메일을 통한 비밀번호 및 링크 기반 로그인을 모두 지원하는 경우 비밀번호/링크 사용자의 로그인 방법을 구별하려면 fetchSignInMethodsForEmail 을 사용하십시오. 이는 사용자에게 먼저 이메일을 제공하라는 요청을 받은 다음 로그인 방법이 제공되는 식별자 우선 흐름에 유용합니다.

Web version 9

import { getAuth, fetchSignInMethodsForEmail, EmailAuthProvider} from "firebase/auth";

// After asking the user for their email.
const email = window.prompt('Please provide your email');

const auth = getAuth();
fetchSignInMethodsForEmail(auth, email)
  .then((signInMethods) => {
    // This returns the same array as fetchProvidersForEmail but for email
    // provider identified by 'password' string, signInMethods would contain 2
    // different strings:
    // 'emailLink' if the user previously signed in with an email/link
    // 'password' if the user has a password.
    // A user could have both.
    if (signInMethods.indexOf(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/password.
    }
    if (signInMethods.indexOf(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/link.
    }
  })
  .catch((error) => {
    // Some error occurred, you can inspect the code: error.code
  });

Web version 8

// After asking the user for their email.
var email = window.prompt('Please provide your email');
firebase.auth().fetchSignInMethodsForEmail(email)
  .then((signInMethods) => {
    // This returns the same array as fetchProvidersForEmail but for email
    // provider identified by 'password' string, signInMethods would contain 2
    // different strings:
    // 'emailLink' if the user previously signed in with an email/link
    // 'password' if the user has a password.
    // A user could have both.
    if (signInMethods.indexOf(
            firebase.auth.EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/password.
    }
    if (signInMethods.indexOf(
            firebase.auth.EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/link.
    }
  })
  .catch((error) => {
    // Some error occurred, you can inspect the code: error.code
  });

위에서 설명한 것처럼 이메일/비밀번호와 이메일/링크는 로그인 방법이 다른 동일한 firebase.auth.EmailAuthProvider (동일 PROVIDER_ID )로 간주됩니다.

다음 단계

사용자가 처음으로 로그인하면 새 사용자 계정이 생성되어 사용자가 로그인한 자격 증명(즉, 사용자 이름 및 암호, 전화 번호 또는 인증 공급자 정보)에 연결됩니다. 이 새 계정은 Firebase 프로젝트의 일부로 저장되며 사용자 로그인 방식에 관계없이 프로젝트의 모든 앱에서 사용자를 식별하는 데 사용할 수 있습니다.

  • 앱에서 사용자의 인증 상태를 알 수 있는 권장 방법은 Auth 개체에 관찰자를 설정하는 것입니다. 그런 다음 User 개체에서 사용자의 기본 프로필 정보를 가져올 수 있습니다. 사용자 관리 를 참조하십시오.

  • Firebase 실시간 데이터베이스 및 Cloud Storage 보안 규칙 에서 auth 변수에서 로그인한 사용자의 고유 사용자 ID를 가져와 사용자가 액세스할 수 있는 데이터를 제어하는 ​​데 사용할 수 있습니다.

인증 공급자 자격 증명을 기존 사용자 계정에 연결하여 사용자가 여러 인증 공급자를 사용하여 앱에 로그인하도록 허용할 수 있습니다.

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

Web version 9

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web version 8

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});