Uwierzytelnij się w Firebase za pomocą linku e-mail w JavaScript

Możesz użyć uwierzytelniania Firebase, aby zalogować użytkownika, wysyłając mu wiadomość e-mail zawierającą link, który może kliknąć, aby się zalogować. Podczas tego procesu weryfikowany jest również adres e-mail użytkownika.

Logowanie przez e-mail ma wiele zalet:

  • Bezproblemowa rejestracja i logowanie.
  • Mniejsze ryzyko ponownego użycia hasła w różnych aplikacjach, co może podważyć bezpieczeństwo nawet dobrze dobranych haseł.
  • Możliwość uwierzytelnienia użytkownika przy jednoczesnym sprawdzeniu, czy użytkownik jest prawowitym właścicielem adresu e-mail.
  • Użytkownik potrzebuje tylko dostępnego konta e-mail, aby się zalogować. Nie jest wymagane posiadanie numeru telefonu ani konta w mediach społecznościowych.
  • Użytkownik może bezpiecznie zalogować się bez konieczności podawania (lub pamiętania) hasła, co może być uciążliwe na urządzeniu mobilnym.
  • Istniejącego użytkownika, który wcześniej logował się przy użyciu identyfikatora e-mail (hasła lub federacyjnego), można uaktualnić, aby logował się tylko przy użyciu adresu e-mail. Na przykład użytkownik, który zapomniał hasła, nadal może się zalogować bez konieczności resetowania hasła.

Zanim zaczniesz

Jeśli jeszcze tego nie zrobiłeś, skopiuj fragment kodu inicjującego z konsoli Firebase do swojego projektu zgodnie z opisem w artykule Dodawanie Firebase do projektu JavaScript .

Aby logować użytkowników za pomocą łącza e-mail, musisz najpierw włączyć metodę logowania Dostawca poczty e-mail i Łącze e-mail w swoim projekcie Firebase:

  1. W konsoli Firebase otwórz sekcję Auth .
  2. Na karcie Metoda logowania włącz dostawcę adresu e-mail/hasła . Pamiętaj, że logowanie za pomocą adresu e-mail/hasła musi być włączone, aby można było korzystać z logowania za pomocą łącza e-mail.
  3. W tej samej sekcji włącz metodę logowania przez łącze e-mail (logowanie bez hasła) .
  4. Kliknij Zapisz .

Aby zainicjować proces uwierzytelniania, przedstaw użytkownikowi interfejs z prośbą o podanie adresu e-mail, a następnie wywołaj funkcję sendSignInLinkToEmail , aby poprosić Firebase o przesłanie linku uwierzytelniającego na adres e-mail użytkownika.

  1. Skonstruuj obiekt ActionCodeSettings , który udostępnia Firebase instrukcje dotyczące tworzenia łącza e-mail. Ustaw następujące pola:

    • url : precyzyjny link do osadzenia i wszelkie dodatkowe informacje, które mają zostać przekazane. Domena linku musi zostać dodana do listy autoryzowanych domen w Konsoli Firebase, którą można znaleźć przechodząc do zakładki Metoda logowania (Uwierzytelnianie -> Metoda logowania).
    • android i ios : aplikacje używane po otwarciu łącza logowania na urządzeniu z Androidem lub Apple. Dowiedz się więcej o konfigurowaniu linków dynamicznych Firebase do otwierania linków działań w wiadomościach e-mail za pośrednictwem aplikacji mobilnych.
    • handleCodeInApp : Ustaw na true. Operacja logowania musi być zawsze zakończona w aplikacji, w przeciwieństwie do innych działań e-mail poza pasmem (resetowanie hasła i weryfikacja adresu e-mail). Wynika to z faktu, że na końcu przepływu oczekuje się, że użytkownik będzie zalogowany, a jego stan uwierzytelnienia zostanie utrwalony w aplikacji.
    • dynamicLinkDomain : jeśli dla projektu zdefiniowano wiele niestandardowych domen linków dynamicznych, określ, która z nich ma być używana, gdy link ma zostać otwarty za pośrednictwem określonej aplikacji mobilnej (na przykład example.page.link ). W przeciwnym razie pierwsza domena jest wybierana automatycznie.

      Web modular API

      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 namespaced API

      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'
      };

    Aby dowiedzieć się więcej o ActionCodeSettings, zapoznaj się z sekcją Passing State in Email Actions .

  2. Poproś użytkownika o jego adres e-mail.

  3. Wyślij link uwierzytelniający na adres e-mail użytkownika i zapisz adres e-mail użytkownika na wypadek, gdyby użytkownik ukończył logowanie się za pomocą poczty e-mail na tym samym urządzeniu.

    Web modular API

    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 namespaced API

    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;
        // ...
      });

Względy bezpieczeństwa

Aby uniemożliwić użycie łącza logowania do zalogowania się jako niezamierzony użytkownik lub na niezamierzonym urządzeniu, Firebase Auth wymaga podania adresu e-mail użytkownika podczas procesu logowania. Aby logowanie się powiodło, ten adres e-mail musi być zgodny z adresem, na który pierwotnie wysłano łącze logowania.

Możesz usprawnić ten przepływ dla użytkowników, którzy otwierają łącze logowania na tym samym urządzeniu, na którym żądają łącza, przechowując ich adresy e-mail lokalnie — na przykład za pomocą localStorage lub plików cookie — podczas wysyłania wiadomości e-mail logowania. Następnie użyj tego adresu, aby zakończyć przepływ. Nie przekazuj adresu e-mail użytkownika w parametrach adresu URL przekierowania i nie używaj go ponownie, ponieważ może to umożliwić wstrzykiwanie sesji.

Po zakończeniu logowania wszelkie poprzednie niezweryfikowane mechanizmy logowania zostaną usunięte z użytkownika, a wszystkie istniejące sesje zostaną unieważnione. Na przykład, jeśli ktoś wcześniej utworzył niezweryfikowane konto z tym samym adresem e-mail i hasłem, hasło użytkownika zostanie usunięte, aby uniemożliwić osobie podszywającej się pod właściciela, która utworzyła to niezweryfikowane konto, ponowne zalogowanie się przy użyciu niezweryfikowanego adresu e-mail i hasła.

Upewnij się również, że używasz adresu URL HTTPS w produkcji, aby uniknąć potencjalnego przechwycenia łącza przez serwery pośredniczące.

Kończenie logowania na stronie internetowej

Format precyzyjnego łącza w wiadomości e-mail jest taki sam, jak format używany w przypadku działań e-mail poza pasmem (weryfikacja adresu e-mail, resetowanie hasła i cofnięcie zmiany adresu e-mail). Firebase Auth upraszcza to sprawdzanie, udostępniając interfejs API isSignInWithEmailLink w celu sprawdzenia, czy link jest linkiem do logowania za pomocą adresu e-mail.

Aby dokończyć logowanie na stronie docelowej, wywołaj funkcję signInWithEmailLink z adresem e-mail użytkownika i faktycznym linkiem e-mail zawierającym jednorazowy kod.

Web modular API

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 namespaced API

// 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.
    });
}

Kończenie logowania w aplikacji mobilnej

Uwierzytelnianie Firebase używa linków dynamicznych Firebase do wysyłania łącza e-mail na urządzenie mobilne. Aby ukończyć logowanie za pomocą aplikacji mobilnej, aplikacja musi być skonfigurowana tak, aby wykrywała przychodzące łącze do aplikacji, analizowała powiązane głębokie łącze, a następnie dopełniała logowania, tak jak odbywa się to za pośrednictwem przepływu sieciowego.

Aby dowiedzieć się więcej o tym, jak obsługiwać logowanie za pomocą łącza e-mail w aplikacji na Androida, zapoznaj się z przewodnikiem Androida .

Aby dowiedzieć się więcej o tym, jak obsługiwać logowanie za pomocą łącza e-mail w aplikacji Apple, zapoznaj się z przewodnikiem po platformach Apple .

Możesz także powiązać tę metodę uwierzytelniania z istniejącym użytkownikiem. Na przykład użytkownik uwierzytelniony wcześniej u innego dostawcy, na przykład za pomocą numeru telefonu, może dodać tę metodę logowania do swojego istniejącego konta.

Różnica byłaby w drugiej połowie operacji:

Web modular API

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 namespaced API

// 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.
  });

Można tego również użyć do ponownego uwierzytelnienia użytkownika łącza e-mail przed uruchomieniem poufnej operacji.

Web modular API

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 namespaced API

// 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.
  });

Ponieważ jednak przepływ może zakończyć się na innym urządzeniu, na którym pierwotny użytkownik nie był zalogowany, przepływ ten może nie zostać ukończony. W takim przypadku użytkownikowi może zostać wyświetlony komunikat o błędzie, aby zmusić go do otwarcia łącza na tym samym urządzeniu. W łączu można przekazać pewien stan w celu dostarczenia informacji o typie operacji i identyfikatorze użytkownika.

Jeśli obsługujesz zarówno logowanie za pomocą hasła, jak i linku za pomocą poczty e-mail, aby rozróżnić metodę logowania dla użytkownika hasła/linku, użyj fetchSignInMethodsForEmail . Jest to przydatne w przypadku przepływów opartych na identyfikatorze, w których użytkownik jest najpierw proszony o podanie adresu e-mail, a następnie przedstawia metodę logowania:

Web modular API

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 namespaced API

// 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
  });

Jak opisano powyżej, adres e-mail/hasło i adres e-mail/link są uważane za tego samego firebase.auth.EmailAuthProvider (ten sam PROVIDER_ID ) z różnymi metodami logowania.

Następne kroki

Gdy użytkownik zaloguje się po raz pierwszy, tworzone jest nowe konto użytkownika, które jest łączone z poświadczeniami — czyli nazwą użytkownika i hasłem, numerem telefonu lub informacjami o dostawcy uwierzytelniania — za pomocą których użytkownik się logował. To nowe konto jest przechowywane jako część Twojego projektu Firebase i może służyć do identyfikacji użytkownika w każdej aplikacji w Twoim projekcie, niezależnie od tego, jak użytkownik się loguje.

  • W twoich aplikacjach zalecanym sposobem poznania statusu autoryzacji użytkownika jest ustawienie obserwatora na obiekcie Auth . Następnie można uzyskać podstawowe informacje o profilu użytkownika z obiektu User . Zobacz Zarządzanie użytkownikami .

  • W regułach bezpieczeństwa Firebase Realtime Database i Cloud Storage możesz uzyskać unikalny identyfikator zalogowanego użytkownika ze zmiennej auth i użyć go do kontrolowania, do jakich danych użytkownik ma dostęp.

Możesz zezwolić użytkownikom na logowanie się do Twojej aplikacji przy użyciu wielu dostawców uwierzytelniania, łącząc poświadczenia dostawcy uwierzytelniania z istniejącym kontem użytkownika.

Aby wylogować użytkownika, wywołaj signOut :

Web modular API

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

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

Web namespaced API

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