Uwierzytelnianie w Firebase za pomocą linku e-mail w JavaScript

Użyj Uwierzytelniania Firebase, aby zalogować użytkownika. Wyślij mu e-maila z linkiem, który umożliwi mu zalogowanie się. W ramach tego procesu weryfikowany jest też adres e-mail użytkownika.

Logowanie się za pomocą adresu e-mail ma wiele zalet:

  • Rejestracja i logowanie bez zbędnych formalności.
  • mniejsze ryzyko ponownego użycia hasła w różnych aplikacjach, co może obniżyć bezpieczeństwo nawet dobrze dobranych haseł;
  • Umożliwia uwierzytelnianie użytkownika i weryfikowanie, czy jest on prawowitym właścicielem adresu e-mail.
  • Aby się zalogować, użytkownik potrzebuje tylko dostępu do konta e-mail. Nie musisz być właścicielem numeru telefonu ani konta w mediach społecznościowych.
  • Użytkownik może logować się bezpiecznie bez konieczności podawania (lub zapamiętywania) hasła, co może być uciążliwe na urządzeniu mobilnym.
  • Istniejący użytkownik, który wcześniej logował się przy użyciu identyfikatora e-mail (hasła lub federacji), może przejść na logowanie się tylko za pomocą adresu e-mail. Na przykład użytkownik, który zapomniał hasło, może się zalogować bez konieczności jego resetowania.

Zanim zaczniesz

Skopiuj fragment kodu inicjalizacji z Firebase konsoli do projektu zgodnie z instrukcjami podanymi w artykule Dodaj Firebase do projektu JavaScript.

Aby umożliwić użytkownikom logowanie się za pomocą linku e-mail, musisz najpierw włączyć w projekcie Firebase dostawcę e-maili i metodę logowania za pomocą linku e-mail:

  1. W konsoli Firebase otwórz sekcję Autoryzacja.
  2. Na karcie Metoda logowania włącz dostawcę E-mail/hasło. Pamiętaj, że aby korzystać z logowania za pomocą linku w e-mailu, musisz włączyć logowanie za pomocą adresu e-mail i hasła.
  3. W tej samej sekcji włącz metodę logowania Link w e-mailu (logowanie bez hasła).
  4. Kliknij Zapisz.

Aby zainicjować proces uwierzytelniania, wyświetl użytkownikowi interfejs, który poprosi go o podanie adresu e-mail, a następnie wywołaj funkcję sendSignInLinkToEmail, aby poprosić Firebase o wysłanie linku uwierzytelniania na adres e-mail użytkownika.

  1. Utwórz obiekt ActionCodeSettings, który zawiera instrukcje dotyczące tworzenia linku e-maila. Ustaw wartości w tych polach:

    • url: precyzyjny link do osadzenia i dodatkowe stany, które mają być przekazywane. Domena linku musi zostać dodana do listy autoryzowanych domen w konsoli Firebase. Znajdziesz ją na karcie Metoda logowania (Uwierzytelnianie -> Ustawienia).
    • androidios: pomagają Firebase Authentication określić, czy mają utworzyć link tylko do strony internetowej czy do strony mobilnej, który będzie otwierany na urządzeniu z Androidem lub Apple.
    • handleCodeInApp: ustaw na wartość true. W przeciwieństwie do innych działań wykonywanych poza aplikacją (takich jak resetowanie hasła czy weryfikacja adresu e-mail) operacja logowania musi być zawsze wykonywana w aplikacji. Dzieje się tak, ponieważ na końcu procesu użytkownik powinien być zalogowany, a stan autoryzacji powinien być zachowany w aplikacji.
    • linkDomain: jeśli w projekcie zdefiniowano niestandardowe Hostingdomeny linków, określ, której z nich chcesz używać, gdy link ma być otwierany przez określoną aplikację mobilną. W przeciwnym razie domyślnie zostanie wybrana domena domyślna (np. PROJECT_ID.firebaseapp.com).
    • dynamicLinkDomain: wycofany. Nie podawaj tego parametru.

      Web

      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

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

    Więcej informacji o ActionCodeSettings znajdziesz w sekcji Przekazywanie stanu w działaniach e-mail.

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

  3. Wyślij link uwierzytelniania na adres e-mail użytkownika i zapisz ten adres na wypadek, gdyby użytkownik chciał zalogować się za pomocą adresu e-mail na tym samym urządzeniu.

    Web

    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

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

Potencjalne problemy z bezpieczeństwem

Aby zapobiec używaniu linku logowania przez nieuprawnionego użytkownika lub na nieuprawnionym urządzeniu, usługa Firebase Auth wymaga podania adresu e-mail użytkownika podczas procesu logowania. Aby zalogować się na konto, musisz podać adres e-mail, na który został wysłany link do logowania.

Możesz uprościć ten proces w przypadku użytkowników, którzy otwierają link logowania na tym samym urządzeniu, na którym wysłali prośbę o link, przechowując ich adres e-mail lokalnie (np. za pomocą localStorage lub plików cookie) podczas wysyłania e-maila z linkiem. Następnie użyj tego adresu, aby dokończyć proces. Nie przekazuj adresu e-mail użytkownika w parametrach adresu URL przekierowania ani nie używaj go ponownie, ponieważ może to umożliwić wstrzyknięcie sesji.

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

Pamiętaj też, aby w produkcji używać adresu URL HTTPS, aby uniknąć przechwycenia linku przez serwery pośredniczące.

Zalogowanie się na stronie

Format precyzyjnego linku do e-maila jest taki sam jak format używany do działań e-mail poza kanałem (weryfikacja adresu e-mail, resetowanie hasła i odwoływanie zmiany adresu e-mail). Firebase Auth upraszcza to sprawdzanie, udostępniając interfejs API isSignInWithEmailLink, który umożliwia sprawdzenie, czy link to link logowania za pomocą adresu e-mail.

Aby dokończyć logowanie na stronie docelowej, zadzwoń pod numer signInWithEmailLink, podając adres e-mail użytkownika i link do e-maila zawierającego kod jednorazowy.

Web

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 by importing getAdditionalUserInfo
      // and calling it with result:
      // getAdditionalUserInfo(result)
      // You can access the user's profile via:
      // getAdditionalUserInfo(result)?.profile
      // You can check if the user is new or existing:
      // getAdditionalUserInfo(result)?.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

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

Logowanie w aplikacji mobilnej

Firebase Authentication używa Firebase Hosting do wysyłania e-maila z linkiem na urządzenie mobilne. Aby umożliwić logowanie się za pomocą aplikacji mobilnej, musisz skonfigurować aplikację tak, aby wykrywała przychodzący link aplikacji, analizowała podłączony precyzyjny link, a następnie przeprowadzała logowanie w taki sam sposób jak w przepływie internetowym.

Więcej informacji o logowaniu się za pomocą linku e-mail w aplikacji na Androida znajdziesz w przewodniku po Androidzie.

Więcej informacji o logowaniu się za pomocą linku e-mail w aplikacji Apple znajdziesz w przewodniku po platformach Apple.

Możesz też połączyć tę metodę uwierzytelniania z dotychczasowym użytkownikiem. Na przykład użytkownik, który wcześniej uwierzytelnił się u innego dostawcy, np. za pomocą numeru telefonu, może dodać tę metodę logowania do swojego dotychczasowego konta.

Różnica polega na tym, że druga połowa operacji:

Web

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

// 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 go też użyć do ponownego uwierzytelnienia użytkownika, który używa linku e-mail, przed wykonaniem operacji wrażliwej.

Web

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

// 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 proces może zakończyć się na innym urządzeniu, na którym pierwotny użytkownik nie był zalogowany, może on nie zostać ukończony. W takim przypadku użytkownik może zobaczyć komunikat o błędzie, który zmusza go do otwarcia linku na tym samym urządzeniu. W linku można przekazać pewien stan, aby podać informacje o typie operacji i identyfikatorze użytkownika (UID).

Jeśli projekt został utworzony 15 września 2023 r. lub później, ochrona przed enumeracją adresów e-mail jest domyślnie włączona. Ta funkcja zwiększa bezpieczeństwo kont użytkowników projektu, ale wyłącza metodę fetchSignInMethodsForEmail(), która była wcześniej zalecana do implementacji procesów opartych na identyfikatorze.

Możesz wyłączyć ochronę przed wyliczaniem adresów e-mail w przypadku swojego projektu, ale nie zalecamy tego.

Aby dowiedzieć się więcej, zapoznaj się z dokumentacją dotyczącą ochrony przed wyliczaniem adresów e-mail.

Domyślny szablon e-maila do logowania z użyciem linku

Domyślny szablon e-maila zawiera sygnaturę czasową w temacie i treści e-maila, aby kolejne e-maile nie były łączone w jeden wątek, a link nie był ukryty.

Ten szablon dotyczy tych języków:

Kod Język
ar arabski
zh-CN chiński (uproszczony)
zh-TW chiński (tradycyjny)
nl niderlandzki
en angielski
en-GB angielski (Wlk. Brytania)
fr francuski
de niemiecki
id Indonezyjski
jego włoski
ja japoński
ko koreański
pl polski
pt-BR portugalski (Brazylia)
pt-PT portugalski (Portugalia)
ru rosyjski
es hiszpański
es-419 hiszpański (Ameryka Łacińska)
th tajski

Dalsze kroki

Gdy użytkownik zaloguje się po raz pierwszy, zostanie utworzone nowe konto użytkownika i połączone z danymi logowania, czyli nazwą użytkownika i hasłem, numerem telefonu lub informacjami dostawcy uwierzytelniania, za pomocą których użytkownik się zalogował. To nowe konto jest przechowywane w ramach projektu Firebase i może służyć do identyfikowania użytkownika we wszystkich aplikacjach w projekcie, niezależnie od tego, jak użytkownik się loguje.

  • W aplikacjach zalecany sposób sprawdzania stanu uwierzytelniania użytkownika to ustawienie obserwatora w obiekcie Auth. Następnie możesz uzyskać podstawowe informacje o profilu użytkownika z obiektu User. Zobacz Zarządzanie użytkownikami.

  • W regułach Firebase Realtime DatabaseCloud Storage Regułach bezpieczeństwa możesz pobrać z zmiennej auth unikalny identyfikator zalogowanego użytkownika i używać go do kontrolowania dostępu użytkownika do danych.

Możesz zezwolić użytkownikom na logowanie się w aplikacji za pomocą danych logowania od wielu dostawców uwierzytelniania, połączając je z dotychczasowym kontem użytkownika.

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

Web

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

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

Web

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