Apple プラットフォームでメールリンクを使用して Firebase 認証を行う

Firebase Authentication を使用すると、ログイン用のリンクを含むメールをユーザーに送信し、ログインしてもらうことができます。このプロセスでは、ユーザーのメールアドレスの検証も行います。

メールでのログインには、次のような利点があります。

  • 登録とログインが簡単になります。
  • アプリ間でパスワードが再利用されるリスクが低くなります。パスワードを再利用すると、適切なパスワードを選択していても、セキュリティが低下するおそれがあります。
  • ユーザー認証で、ユーザーがメールアドレスの正当な所有者であることも確認できます。
  • アクセス可能なメール アカウントがあればそれだけでログインできます。電話番号やソーシャル メディアのアカウントは必要ありません。
  • パスワードを入力(あるいは記憶)しなくても、安全にログインできます。モバイル デバイスでは、パスワードの入力や記憶が面倒な場合があります。
  • 前にメール ID(パスワードや認証連携)でログインしたユーザーをメールのみによるログインにアップグレードできます。たとえば、パスワードを忘れてしまった場合、パスワードを再設定しなくてもログインできます。

始める前に

Swift Package Manager を使用して Firebase の依存関係をインストールし、管理します。

  1. Xcode でアプリのプロジェクトを開いたまま、[File] > [Add Packages] の順に移動します。
  2. プロンプトが表示されたら、Firebase Apple プラットフォーム SDK リポジトリを追加します。
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. Firebase Authentication ライブラリを選択します。
  5. ターゲットのビルド設定の [Other Linker Flags] セクションに -ObjC フラグを追加します。
  6. 上記の作業が完了すると、Xcode は依存関係の解決とバックグラウンドでのダウンロードを自動的に開始します。

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

  1. Firebase コンソールで [Authentication] セクションを開きます。
  2. [Sign-in method] タブで [メール / パスワード] を有効にします。メールリンク ログインを使用するには、メール / パスワードによるログインを有効にする必要があります。
  3. 同じセクションで、ログイン方法として [メールリンク(パスワードなしでログイン)] を有効にします。
  4. [保存] をクリックします。

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

  1. メールリンクの作成方法を Firebase に伝える ActionCodeSettings オブジェクトを作成します。次のフィールドを設定します。

    • url: メールに埋め込むディープリンク。状態も一緒に渡します。リンクのドメインは、Firebase コンソールで承認済みドメインの許可リストに登録する必要があります。これは、[Sign-in method] タブ([Authentication] > [Sign-in method])で行えます。
    • iOSBundleIDandroidPackageName: Android または Apple デバイスで開くウェブ専用リンクとモバイルリンクのどちらを作成するかを Firebase Authentication が判断するのに役立ちます。
    • handleCodeInApp: true に設定します。他の帯域外メール アクション(パスワードのリセットやメールアドレスの確認など)と異なり、ログインはアプリ内で完結させる必要があります。これは、フローの最後でユーザーがログインに成功し、認証状態がアプリ内で維持される必要があるためです。
    • linkDomain: プロジェクトにカスタム Hosting リンク ドメインが定義されている場合は、指定したモバイルアプリでリンクを開く際に使用するドメインを指定します。指定しない場合、デフォルトのドメインが自動的に選択されます(PROJECT_ID.firebaseapp.com など)。
    • dynamicLinkDomain: 非推奨。このパラメータは指定しないでください。

    Swift

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.url = URL(string: "https://www.example.com")
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
    actionCodeSettings.setAndroidPackageName("com.example.android",
                                             installIfNotAvailable: false, minimumVersion: "12")

    Objective-C

    FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
    [actionCodeSettings setURL:[NSURL URLWithString:@"https://www.example.com"]];
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = YES;
    [actionCodeSettings setIOSBundleID:[[NSBundle mainBundle] bundleIdentifier]];
    [actionCodeSettings setAndroidPackageName:@"com.example.android"
                        installIfNotAvailable:NO
                               minimumVersion:@"12"];

    ActionCodeSettings の詳細については、メール アクションで状態を渡すをご覧ください。

  2. ユーザーにメールアドレスを求めます。

  3. ユーザーのメールアドレスに認証リンクを送信し、ユーザーが同じデバイスでメールによるログインを完了する場合に備えてこのメールアドレスを保存します。

    Swift

    Auth.auth().sendSignInLink(toEmail: email,
                               actionCodeSettings: actionCodeSettings) { error in
      // ...
        if let error = error {
          self.showMessagePrompt(error.localizedDescription)
          return
        }
        // 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.
        UserDefaults.standard.set(email, forKey: "Email")
        self.showMessagePrompt("Check your email for link")
        // ...
    }

    Objective-C

    [[FIRAuth auth] sendSignInLinkToEmail:email
                       actionCodeSettings:actionCodeSettings
                               completion:^(NSError *_Nullable error) {
      // ...
        if (error) {
          [self showMessagePrompt:error.localizedDescription];
           return;
        }
        // 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.
        [NSUserDefaults.standardUserDefaults setObject:email forKey:@"Email"];
        [self showMessagePrompt:@"Check your email for link"];
        // ...
    }];

セキュリティに関する懸念

意図しないユーザーやデバイスがログインリンクでログインしないように、Firebase Auth はログインフローの完了時にユーザーにメールアドレスの入力を求めます。入力されたメールアドレスがログインリンクの送信先アドレスと一致しないと、ログインに失敗します。

ログインメールの送信時に、メールアドレスをローカルに保存しておくことで、ログインリンクをリクエストした時に使用したものと同じデバイスでログインリンクを開くユーザーの認証フローが簡単になります。メールアドレスが一致した場合、このアドレスを使用してフローを完了します。

ログインが完了すると、未確認のログインはすべて破棄され、既存のセッションが無効になります。たとえば、未確認のアカウントが同じメールとパスワードですでに作成されている場合、未確認アカウントを作成した人物が所有者を装って同じアカウントでログインできないように、ユーザーのパスワードが削除されます。

Apple モバイルアプリでログインを完了する

Firebase AuthenticationFirebase Hosting を使用して、モバイル デバイスにメールリンクを送信します。モバイルアプリ経由でログインを行う場合、アプリリンクの受信を検出し、ディープリンクを解析してログインを完了するようにアプリを構成する必要があります。詳しくは、iOS のユニバーサル リンクと関連ドメインをご覧ください。

Firebase Hosting の設定

Firebase Authentication は、モバイルアプリで開くリンクを作成して送信するときに Firebase Hosting ドメインを使用します。デフォルトの Firebase Hosting ドメインはすでに構成されています。

  1. Firebase Hosting ドメインを構成します。

    Firebase コンソールで [ホスティング] セクションを開きます。

    • モバイル アプリで開くメールリンクにデフォルト ドメインを使用する場合は、デフォルト サイトに移動して、デフォルトの Hosting ドメインをメモします。デフォルトの Hosting ドメインは通常、PROJECT_ID.firebaseapp.com のようになります。

      受信リンクをインターセプトするようにアプリを構成するときに、この値が必要になります。

    • メールリンクにカスタム ドメインを使用する場合は、Firebase Hosting でドメインを登録し、そのドメインをリンクのドメインとして使用できます。

  2. Apple アプリを構成する方法:

    選択したドメインをアプリリンクの関連ドメインとして構成する必要があります。アプリで利用資格を設定するには、Xcode でターゲットの [Signing & Capabilities] タブを開き、前の手順で取得した Firebase Hosting ドメインを [Associated Domains] 機能に追加します。デフォルトの Firebase Hosting ドメインを使用している場合は、applinks:PROJECT_ID.firebaseapp.com になります。

    詳しくは、Apple のドキュメント サイトで関連付けられたドメインのサポートをご覧ください。

上記のリンクを受信したら、このリンクがメールリンク認証用であることを確認して、ログインを完了します。

Swift

if Auth.auth().isSignIn(withEmailLink: link) {
        Auth.auth().signIn(withEmail: email, link: self.link) { user, error in
          // ...
        }
}

Objective-C

if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
    [[FIRAuth auth] signInWithEmail:email
                               link:link
                         completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      // ...
    }];
}

Android アプリでメールリンクによるログインを処理する方法については、Android のガイドをご覧ください。

ウェブアプリでメールリンクによるログインを処理する方法については、ウェブのガイドをご覧ください。

この認証方法は既存ユーザーの認証でも利用できます。たとえば、ユーザーが別のプロバイダにおいて電話番号などで認証されている場合、既存のアカウントにメールリンクによる認証方法を追加できます。

オペレーションの後半部分が異なります。

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.link(with: credential) { authData, error in
    if (error) {
      // And error occurred during linking.
      return
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthProvider credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkWithCredential:credential
              completion:^(FIRAuthDataResult *_Nullable result,
                           NSError *_Nullable error) {
    if (error) {
      // And error occurred during linking.
      return;
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }];

これは、重要なオペレーションの前にメールリンク ユーザーを再認証する場合にも使用できます。

Swift

  let credential = EmailAuthProvider.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticate(with: credential) { authData, error in
    if (error) {
      // And error occurred during re-authentication.
      return
    }
    // The user was successfully re-authenticated.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      reauthenticateWithCredential:credential
                        completion:^(FIRAuthDataResult *_Nullable result,
                                     NSError *_Nullable error) {
    if (error) {
      // And error occurred during re-authentication
      return;
    }
    // The user was successfully re-authenticated.
  }];

ただし、元のユーザーがログインしていない別のデバイスでフローを完了しようとすると、フローが完了できなくなります。この場合、ユーザーにエラーを表示し、強制的に同じデバイスでリンクを開かせます。オペレーションの種類やユーザーの UID の情報を提供するため、リンクと一緒に状態を渡すこともできます。

Firebase Authentication iOS SDK v11.8.0 より前は、メールリンクによるログイン機能は Firebase Dynamic Links を使用して、正しいアプリでログインリンクを開いていました。Firebase Dynamic Links2025 年 8 月 25 日に廃止されるため、これらの確認リンクは非推奨となります。

アプリで従来のリンクを使用している場合は、新しい Firebase Hosting ベースのシステムにアプリを移行する必要があります。

2023 年 9 月 15 日以降にプロジェクトを作成した場合、メール列挙保護はデフォルトで有効になっています。この機能により、プロジェクトのユーザー アカウントのセキュリティが強化されますが、以前は ID 優先フローを実装する際に推奨されていた fetchSignInMethodsForEmail() メソッドが無効になります。

プロジェクトでメール列挙保護を無効にすることはできますが、無効にしないことをおすすめします。

詳細については、メール列挙保護を有効または無効にするをご覧ください。

次のステップ

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

  • アプリでは、User オブジェクトからユーザーの基本的なプロフィール情報を取得できます。ユーザーを管理するをご覧ください。

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

既存のユーザー アカウントに認証プロバイダの認証情報をリンクすることで、ユーザーは複数の認証プロバイダを使用してアプリにログインできるようになります。

ユーザーのログアウトを行うには、signOut: を呼び出します。

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

さまざまな認証エラーに対応できるようにエラー処理コードを追加することもできます。エラーの処理をご覧ください。