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

Możesz użyć Uwierzytelniania Firebase, aby zalogować użytkownika, wysyłając mu e-maila z linkiem, który może kliknąć, aby się zalogować. W trakcie tego procesu weryfikowany jest też adres e-mail użytkownika.

Logowanie się za pomocą adresu e-mail ma wiele korzyści:

  • Łatwa rejestracja i logowanie.
  • Mniejsze ryzyko ponownego użycia hasła w różnych aplikacjach, co może osłabić bezpieczeństwo nawet dobrze dobranych haseł.
  • Możliwość uwierzytelnienia użytkownika przy jednoczesnym sprawdzeniu, czy jest on prawowitym właścicielem adresu e-mail.
  • Aby się zalogować, użytkownik potrzebuje tylko dostępnego konta e-mail. Nie musi mieć numeru telefonu ani konta w mediach społecznościowych.
  • Użytkownik może bezpiecznie się zalogować bez konieczności podawania (ani zapamiętywania) hasła, co może być uciążliwe na urządzeniu mobilnym.
  • Dotychczasowy użytkownik, który wcześniej logował się za pomocą identyfikatora e-mail (hasła lub federacyjnego), może zostać uaktualniony, aby logować się tylko za pomocą adresu e-mail. Jeśli np. użytkownik zapomni hasła, nadal będzie mógł się zalogować bez konieczności jego resetowania.

Zanim zaczniesz

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

Aby logować użytkowników za pomocą linku w e-mailu, musisz najpierw włączyć dostawcę poczty e-mail i metodę logowania za pomocą linku w e-mailu w projekcie w Firebase:

  1. W konsoli Firebase otwórz Zabezpieczenia > Uwierzytelnianie.

  2. Na karcie Metoda logowania włącz metodę logowania Adres e-mail i 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 dostawcę logowania Link w e-mailu (logowanie bez hasła).

  4. Kliknij Zapisz.

Aby rozpocząć proces uwierzytelniania, wyświetl użytkownikowi interfejs, w którym będzie on proszony o podanie adresu e-mail, a następnie wywołaj funkcję sendSignInLinkToEmail, aby poprosić Firebase o wysłanie linku uwierzytelniającego na adres e-mail użytkownika.

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

    • url: precyzyjny link do osadzenia i dodatkowy stan do przekazania. Jeśli jeszcze tego nie zrobisz, dodaj swoją domenę do listy autoryzowanych domen:

      1. W konsoli Firebase otwórz kartę Zabezpieczenia > Uwierzytelnianie > Ustawienia.

      2. W sekcji Autoryzowane domeny kliknij Dodaj domenę i dodaj swoją domenę.

    • android i ios: pomagają usłudze Firebase Authentication określić, czy ma utworzyć link tylko do witryny, czy link mobilny, który zostanie otwarty na urządzeniu z Androidem lub Apple.
    • handleCodeInApp: ustaw na true. W przeciwieństwie do innych działań wykonywanych poza pasmem (resetowanie hasła i weryfikacja adresu e-mail) operację logowania trzeba zawsze dokończyć w aplikacji. Dzieje się tak, ponieważ na końcu procesu użytkownik powinien być zalogowany, a jego stan uwierzytelnienia powinien być zachowany w aplikacji.
    • linkDomain: jeśli w projekcie zdefiniowano niestandardowe domeny linków Hosting, określ, której z nich należy użyć, gdy link ma zostać otwarty w określonej aplikacji mobilnej. W przeciwnym razie automatycznie 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'
        },
        // The domain must be configured in Firebase Hosting and owned by the project.
        linkDomain: 'custom-domain.com'
      };

      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 związanych z e-mailami.

  2. Poproś użytkownika o podanie adresu e-mail.

  3. Wyślij link uwierzytelniający na adres e-mail użytkownika i zapisz ten adres na wypadek, gdyby użytkownik dokończył logowanie za pomocą e-maila 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 uniemożliwić użycie linku do logowania w celu zalogowania się jako niepożądany użytkownik lub na niepożądanym urządzeniu, usługa Uwierzytelnianie Firebase wymaga podania adresu e-mail użytkownika podczas dokonywania logowania. Aby logowanie się powiodło, ten adres e-mail musi być zgodny z adresem, na który pierwotnie został wysłany link do logowania.

Możesz uprościć ten proces dla użytkowników, którzy otwierają link do logowania na tym samym urządzeniu, na którym go żądają, zapisując ich adres e-mail lokalnie – np. za pomocą localStorage lub plików cookie – podczas wysyłania e-maila z linkiem do logowania. Następnie użyj tego adresu, aby dokończyć proces. 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 wszystkie poprzednie niezweryfikowane mechanizmy logowania zostaną usunięte z konta użytkownika, a wszystkie istniejące sesje zostaną unieważnione. Jeśli np. 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ć oszustowi, który przejął własność i utworzył to niezweryfikowane konto, ponowne zalogowanie się za pomocą niezweryfikowanego adresu e-mail i hasła.

Upewnij się też, że w środowisku produkcyjnym używasz adresu URL HTTPS, aby uniknąć przechwycenia linku przez serwery pośredniczące.

Dokończenie logowania na stronie internetowej

Format precyzyjnego linku w e-mailu jest taki sam jak format używany w przypadku działań związanych z e-mailami wykonywanych poza pasmem (weryfikacja adresu e-mail, resetowanie hasła i cofnięcie zmiany adresu e-mail). Usługa Uwierzytelnianie Firebase upraszcza to sprawdzanie, udostępniając interfejs API isSignInWithEmailLink, który pozwala sprawdzić, czy link jest linkiem do logowania za pomocą e-maila.

Aby dokończyć logowanie na stronie docelowej, wywołaj funkcję signInWithEmailLink, podając adres e-mail użytkownika i rzeczywisty link w e-mailu zawierający 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.
    });
}

Dokończenie logowania w aplikacji mobilnej

Firebase Authentication używa Firebase Hosting do wysyłania linku w e-mailu na urządzenie mobilne. Aby dokończyć logowanie za pomocą aplikacji mobilnej, aplikacja musi być skonfigurowana tak, aby wykrywała przychodzący link do aplikacji, analizowała precyzyjny link i dokonywała logowania tak jak w przypadku procesu internetowego.

Więcej informacji o tym, jak obsługiwać logowanie za pomocą linku w e-mailu w aplikacji na Androida , znajdziesz w przewodniku po Androidzie.

Więcej informacji o tym, jak obsługiwać logowanie za pomocą linku w e-mailu w aplikacji Apple , znajdziesz w przewodniku po platformach Apple.

Możesz też połączyć tę metodę uwierzytelniania z istniejącym 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 będzie widoczna w drugiej połowie 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żesz też użyć tej metody do ponownego uwierzytelnienia użytkownika linku w e-mailu przed wykonaniem operacji wymagającej zachowania poufności.

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

Jednak ponieważ proces może się zakończyć na innym urządzeniu, na którym pierwotny użytkownik nie był zalogowany, ten proces może się nie powieść. W takim przypadku użytkownikowi można wyświetlić błąd, aby wymusić otwarcie linku na tym samym urządzeniu. W linku można przekazać pewien stan, aby podać informacje o typie operacji i identyfikatorze UID użytkownika.

Jeśli projekt został utworzony 15 września 2023 r. lub później, ochrona przed wyliczaniem adresów e-mail jest domyślnie włączona. Ta funkcja zwiększa bezpieczeństwo kont użytkowników w projekcie, ale wyłącza metodę fetchSignInMethodsForEmail(), której wcześniej zalecaliśmy używać do implementowania procesów opartych na identyfikatorze.

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

Więcej informacji znajdziesz w dokumentacji dotyczącej ochrony przed wyliczaniem adresów e-mail.

Domyślny szablon e-maila do logowania za pomocą linku

Domyślny szablon e-maila zawiera sygnaturę czasową w temacie i treści e-maila dzięki czemu kolejne e-maile nie są zwijane w jeden wątek, a link nie jest ukryty.

Ten szablon jest dostępny w tych językach:

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
it 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 – nazwą użytkownika i hasłem, numerem telefonu lub informacjami o dostawcy uwierzytelniania – za pomocą których użytkownik się zalogował. To nowe konto jest przechowywane w projekcie w Firebase i może służyć do identyfikowania użytkownika w każdej aplikacji w projekcie, niezależnie od sposobu logowania.

  • W aplikacjach zalecamy ustawienie obserwatora w obiekcie Auth, aby poznać stan uwierzytelnienia użytkownika. Następnie możesz pobrać 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 bazy danych czasu rzeczywistego Firebase i Cloud Storage, możesz pobrać unikalny identyfikator użytkownika zalogowanego z zmiennej auth, i użyć go do kontrolowania, do jakich danych użytkownik ma dostęp.

Możesz zezwolić użytkownikom na logowanie się w aplikacji za pomocą wielu dostawców uwierzytelniania przez połączenie danych logowania dostawcy uwierzytelniania z istniejącym 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.
});