Google は、黒人コミュニティのための人種的公平の促進に取り組んでいます。詳細をご覧ください。

JavaScriptのEメールリンクを使用してFirebaseで認証する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Firebase Authenticationを使用して、ユーザーがクリックしてサインインできるリンクを含むメールを送信することで、ユーザーにログインできます。その過程で、ユーザーのメールアドレスも確認されます。

電子メールでサインインすることには多くの利点があります。

  • 低摩擦のサインアップとサインイン。
  • アプリケーション間でのパスワードの再利用のリスクが低くなります。これにより、適切に選択されたパスワードのセキュリティが損なわれる可能性があります。
  • ユーザーが電子メールアドレスの正当な所有者であることを確認しながら、ユーザーを認証する機能。
  • ユーザーは、アクセス可能な電子メールアカウントのみでサインインできます。電話番号やソーシャルメディアアカウントの所有権は必要ありません。
  • ユーザーは、パスワードを提供(または記憶)することなく安全にサインインできます。これは、モバイルデバイスでは面倒な場合があります。
  • 以前に電子メール識別子(パスワードまたはフェデレーション)でサインインした既存のユーザーは、電子メールだけでサインインするようにアップグレードできます。たとえば、パスワードを忘れたユーザーは、パスワードをリセットしなくてもサインインできます。

あなたが始める前に

まだ行っていない場合は、JavaScriptプロジェクトへのFirebaseの追加の説明に従って、 Firebaseコンソールからプロジェクトに初期化スニペットをコピーします。

メールリンクでユーザーにログインするには、まずFirebaseプロジェクトでメールプロバイダーとメールリンクのログイン方法を有効にする必要があります。

  1. Firebaseコンソールで、[認証]セクションを開きます。
  2. [サインイン方法]タブで、電子メール/パスワードプロバイダーを有効にします。電子メールリンクサインインを使用するには、電子メール/パスワードサインインを有効にする必要があることに注意してください。
  3. 同じセクションで、 Eメールリンク(パスワードなしのサインイン)サインイン方法を有効にします。
  4. [保存]をクリックします。

認証フローを開始するには、ユーザーにメールアドレスの入力を求めるインターフェースをユーザーに提示してから、 sendSignInLinkToEmailを呼び出して、Firebaseがユーザーのメールに認証リンクを送信するようにリクエストします。

  1. ActionCodeSettingsオブジェクトを作成します。これにより、Firebaseにメールリンクの作成方法に関する手順が提供されます。次のフィールドを設定します。

    • url :埋め込みへのディープリンクと渡される追加の状態。リンクのドメインは、Firebase Consoleの承認済みドメインのリストに追加する必要があります。このリストは、[サインイン方法]タブ([認証]-> [サインイン方法])に移動して見つけることができます。
    • androidおよびios :サインインリンクがAndroidまたはAppleデバイスで開かれたときに使用するアプリ。モバイルアプリを介してメールアクションリンクを開くようにFirebaseDynamicLinksを設定する方法の詳細をご覧ください。
    • 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 Authでは、サインインフローの完了時にユーザーのメールアドレスを指定する必要があります。サインインを成功させるには、この電子メールアドレスがサインインリンクが最初に送信されたアドレスと一致している必要があります。

サインイン電子メールを送信するときに、たとえばlocalStorageまたはCookieを使用して、電子メールアドレスをローカルに保存することにより、リンクを要求するのと同じデバイスでサインインリンクを開くユーザーのこのフローを合理化できます。次に、このアドレスを使用してフローを完了します。リダイレクトURLパラメータでユーザーの電子メールを渡さないでください。再利用すると、セッションインジェクションが有効になる可能性があります。

サインインの完了後、以前の未確認のサインインメカニズムはユーザーから削除され、既存のセッションは無効になります。たとえば、誰かが以前に同じ電子メールとパスワードで未確認のアカウントを作成した場合、所有権を主張してその未確認のアカウントを作成したなりすまし者が未確認の電子メールとパスワードで再度サインインするのを防ぐために、ユーザーのパスワードが削除されます。

また、リンクが中間サーバーによって傍受される可能性を回避するために、本番環境ではHTTPSURLを使用するようにしてください。

Webページでのサインインの完了

電子メールリンクのディープリンクの形式は、帯域外の電子メールアクション(電子メールの検証、パスワードのリセット、および電子メールの変更の取り消し)に使用される形式と同じです。 Firebase Authは、リンクがメールリンクを使用したログインであるかどうかを確認する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認証では、FirebaseDynamicLinksを使用してメールリンクをモバイルデバイスに送信します。モバイルアプリケーションを介してサインインを完了するには、着信アプリケーションリンクを検出し、基になるディープリンクを解析してから、Webフローを介して行われるようにサインインを完了するようにアプリケーションを構成する必要があります。

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

上記のように、email/passwordとemail/linkは、サインインの方法が異なる同じfirebase.auth.EmailAuthProvider (同じPROVIDER_ID )と見なされます。

次のステップ

ユーザーが初めてサインインすると、新しいユーザーアカウントが作成され、ユーザーがサインインした資格情報(つまり、ユーザー名とパスワード、電話番号、または認証プロバイダー情報)にリンクされます。この新しいアカウントはFirebaseプロジェクトの一部として保存され、ユーザーのログイン方法に関係なく、プロジェクト内のすべてのアプリでユーザーを識別するために使用できます。

  • アプリで、ユーザーの認証ステータスを知るための推奨される方法は、 Authオブジェクトにオブザーバーを設定することです。次に、 Userオブジェクトからユーザーの基本的なプロファイル情報を取得できます。ユーザーの管理を参照してください。

  • FirebaseRealtimeデータベースとCloudStorageのセキュリティルールでは、ログインしたユーザーの一意のユーザーIDをauth変数から取得し、それを使用してユーザーがアクセスできるデータを制御できます。

認証プロバイダーのクレデンシャルを既存のユーザーアカウントにリンクすることで、ユーザーが複数の認証プロバイダーを使用してアプリにサインインできるようにすることができます。

ユーザーをサインアウトするには、 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.
});