iOS でメールリンクを使用して Firebase 認証を行う

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

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

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

準備

  1. Firebase を iOS プロジェクトに追加しますPodfile に次のポッドをインクルードします。
    pod 'Firebase/Auth'
    
  2. アプリを Firebase プロジェクトに接続していない場合は、Firebase コンソールで接続します。

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

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

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

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

    • url: メールに埋め込むディープリンク。状態も一緒に渡します。リンクのドメインは、Firebase コンソールで承認済みドメインのホワイトリストに登録する必要があります。
    • iOSBundleID と androidPackageName: Android または iOS 端末でログインリンクを開くアプリ。モバイルアプリ経由でメールのアクション リンクを開く方法については、Firebase Dynamic Links の構成をご覧ください。
    • handleCodeInApp: true に設定します。パスワードのリセットやメールアドレスの確認など、他の帯域外メール アクションと異なり、ログインはアプリ内で完結させる必要があります。これは、フローの最後でユーザーがログインに成功し、認証状態がアプリ内で維持される必要があるためです。

    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 はログインフローの完了時にユーザーにメールアドレスの入力を求めます。入力されたメールアドレスがログインリンクの送信アドレスと一致しないと、ログインに失敗します。

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

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

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

Firebase Authentication は、Firebase Dynamic Links を使用してモバイル端末にメールリンクを送信します。モバイルアプリ経由でログインを行う場合、リンクの受信を検出し、ディープリンクを解析してログインを完了するようにアプリを構成する必要があります。

Firebase Auth では、モバイルアプリで開かれるリンクを送信するときに Firebase Dynamic Links が使用されます。この機能を使用するには、Firebase コンソールで Dynamic Links を構成する必要があります。

  1. Firebase Dynamic Links を有効にします。

    1. Firebase コンソールで [Dynamic Links] セクションを開きます。
    2. まだ Dynamic Links の利用規約に同意していない場合は、[使ってみる] を選択します。メインの Dynamic Links ダッシュボードに戻ります。
    3. ダイナミック リンク ドメインをメモしておきます。これは abc123.app.goo.gl のような形式で、受信リンクをインターセプトするように Android または iOS アプリを構成するときに必要になります。
  2. iOS アプリを構成します。

    1. iOS アプリでこれらのリンクを処理する予定の場合は、Firebase コンソールのプロジェクト設定で iOS バンドル ID を指定する必要があります。それに加えて、App Store ID と Apple デベロッパー チーム ID を指定する必要があります。
    2. FDL ユニバーサル リンク ドメインをアプリ機能の関連ドメインとして構成する必要があります。
    3. アプリを iOS バージョン 8 以下に配布する予定がある場合は、iOS バンドル ID を受信 URL のカスタム スキームとして設定する必要があります。
    4. 詳しくは、iOS Dynamic Links の受信の手順をご覧ください。

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

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?.linkAndRetrieveData(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 =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkAndRetrieveDataWithCredential: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 = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticateAndRetrieveData(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
      reauthenticateAndRetrieveDataWithCredential:credential
                                       completion:^(FIRAuthDataResult *_Nullable result,
                                                    NSError *_Nullable error) {
    if (error) {
      // And error occurred during re-authentication
      return;
    }
    // The user was successfully re-authenticated.
  }];

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

メールのパスワードとリンクベースの両方のログイン方法をサポートする場合、fetchSignInMethodsForEmail を使用して、それぞれのログイン方法を区別します。この方法は、ユーザーにメールアドレスの入力を求め、入力後にログイン方法を提示する ID 優先のフローで役立ちます。

Swift

 // After asking the user for their email.
 Auth.auth().fetchSignInMethods(forEmail: email) { signInMethods, error in
   // This returns the same array as fetchProviders(forEmail:completion:) 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 (error) {
     // Handle error case.
   }
   if (!signInMethods.contains(EmailPasswordAuthSignInMethod)) {
     // User can sign in with email/password.
   }
   if (!signInMethods.contains(EmailLinkAuthSignInMethod)) {
     // User can sign in with email/link.
   }
 }

Objective-C

 // After asking the user for their email.
 [FIRAuth auth] fetchSignInMethodsForEmail:email
                                completion:^(NSArray *_Nullable signInMethods,
                                             NSError *_Nullable error) {
   // 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 (error) {
     // Handle error case.
   }
   if (![signInMethods containsObject:FIREmailPasswordAuthSignInMethod]) {
     // User can sign in with email/password.
   }
   if (![signInMethods containsObject:FIREmailLinkAuthSignInMethod]) {
     // User can sign in with email/link.
   }
 }];

上記のように、メール / パスワードとメールリンクは、異なるログイン方法を使用する同じ FIREmailAuthProvider(同じ PROVIDER_ID)と考えることができます。

次のステップ

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

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

  • Firebase Realtime Database と Cloud 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;
}

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

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。