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

Usługa Uwierzytelnianie Firebase umożliwia logowanie użytkowników przez wysyłanie im e-maili z linkiem, który mogą kliknąć, aby się zalogować. W tym procesie weryfikowany jest też adres e-mail użytkownika.

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

  • Bezproblemowa rejestracja i logowanie.
  • Mniejsze ryzyko ponownego użycia haseł w różnych aplikacjach, co może osłabić bezpieczeństwo nawet dobrze dobranych haseł.
  • Możliwość uwierzytelniania użytkownika przy jednoczesnej weryfikacji, czy jest on prawowitym właścicielem adresu e-mail.
  • Aby się zalogować, użytkownik potrzebuje tylko dostępnego konta e-mail. Nie musisz być właścicielem numeru telefonu ani konta w mediach społecznościowych.
  • Użytkownik może bezpiecznie zalogować się bez konieczności podawania (lub 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 logowania sfederowanego), może przejść na logowanie się tylko przy użyciu adresu e-mail. Na przykład użytkownik, który zapomniał hasła, może się zalogować bez konieczności jego resetowania.

Zanim zaczniesz

Jeśli jeszcze tego nie zrobiono, skopiuj fragment kodu inicjującego z Firebasekonsoli do projektu zgodnie z opisem w artykule 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 Bezpieczeństwo > Uwierzytelnianie.

  2. Na karcie Metoda logowania włącz metodę logowania Adres 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 dostawcę tożsamości 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 do uwierzytelniania 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 umieszczenia i wszelkie dodatkowe informacje o stanie, które mają zostać przekazane. Jeśli jeszcze tego nie zrobiono, dodaj domenę do listy autoryzowanych domen:

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

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

    • android i ios: pomagają Firebase Authentication określić, czy ma utworzyć link tylko do internetu czy link mobilny, który będzie otwierany na urządzeniu z Androidem lub Apple.
    • handleCodeInApp: ustaw na wartość „true”. Proces logowania musi zawsze być przeprowadzany w aplikacji, w przeciwieństwie do innych działań wykonywanych poza pasmem za pomocą e-maila (resetowanie hasła i weryfikacja adresu e-mail). Dzieje się tak, ponieważ na końcu procesu użytkownik powinien być zalogowany, a jego stan uwierzytelniania 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 być otwierany przez konkretną aplikację mobilną. W przeciwnym razie automatycznie zostanie wybrana domena domyślna (np. PROJECT_ID.firebaseapp.com).
    • dynamicLinkDomain: wycofano. 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 przez 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życiu linku do logowania w celu zalogowania się jako nieodpowiedni użytkownik lub na nieodpowiednim urządzeniu, Firebase Auth wymaga podania adresu e-mail użytkownika podczas kończenia procesu 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ą. W tym celu zapisz lokalnie ich adres e-mail – na przykład za pomocą localStorage lub plików cookie – gdy wysyłasz 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 dotychczasowe sesje zostaną unieważnione. Jeśli na przykład 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ć podszywającemu się pod niego użytkownikowi, który zgłosił własność i utworzył to niezweryfikowane konto, ponowne zalogowanie się przy użyciu niezweryfikowanego adresu e-mail i hasła.

W środowisku produkcyjnym używaj adresu URL HTTPS, aby uniknąć potencjalnego przechwycenia linku przez serwery pośredniczące.

Kończenie logowania na stronie

Format precyzyjnego linku w e-mailu jest taki sam jak format używany w przypadku działań w e-mailu wykonywanych poza pasmem (potwierdzanie adresu e-mail, resetowanie hasła i cofanie zmiany adresu e-mail). Firebase Auth upraszcza to sprawdzanie, udostępniając interfejs API isSignInWithEmailLink, który pozwala sprawdzić, czy link służy do logowania się za pomocą adresu e-mail.

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

Kończenie logowania w aplikacji mobilnej

Firebase Authentication używa Firebase Hosting, aby wysłać link do e-maila na urządzenie mobilne. Aby dokończyć logowanie w aplikacji mobilnej, musi ona być skonfigurowana tak, aby wykrywać przychodzący link do aplikacji, analizować precyzyjny link i dokończyć logowanie tak jak w przypadku procesu internetowego.

Więcej informacji o obsłudze logowania za pomocą linku w aplikacji na Androida znajdziesz w tym przewodniku.

Więcej informacji o obsłudze logowania za pomocą linku w aplikacji na urządzenia 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 obecnego konta.

Różnica wystąpi 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żna go też użyć do ponownego uwierzytelnienia użytkownika linku e-mail przed wykonaniem operacji wymagającej szczególnej ochrony.

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 proces może zakończyć się na innym urządzeniu, na którym pierwotny użytkownik nie był zalogowany, więc może nie zostać ukończony. W takim przypadku użytkownikowi może zostać wyświetlony błąd, który zmusi 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.

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 projektu, ale wyłącza metodę fetchSignInMethodsForEmail(), którą wcześniej zalecaliśmy do implementowania przepływów opartych na identyfikatorze.

Chociaż możesz wyłączyć ochronę przed wyliczaniem adresów e-mail w swoim projekcie, nie zalecamy tego.

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 ukrywany.

Ten szablon ma zastosowanie 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
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

Po pierwszym zalogowaniu się użytkownika tworzone jest nowe konto użytkownika, które jest powiązane z danymi logowania, czyli 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 ramach projektu w Firebase i może służyć do identyfikowania użytkownika we wszystkich aplikacjach w projekcie, niezależnie od sposobu logowania.

  • Zalecanym sposobem sprawdzania stanu uwierzytelniania użytkownika w aplikacjach jest ustawienie obserwatora na obiekcie Auth. Następnie możesz pobrać podstawowe informacje o profilu użytkownika z obiektu User. Zobacz Zarządzanie użytkownikami.

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

Możesz zezwolić użytkownikom na logowanie się w aplikacji za pomocą wielu dostawców uwierzytelniania, łącząc dane 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.
});