20210304のiOSに関する記事は4件です。

【Swift】バックグラウンド時にもセキュアなiOSアプリを実装する

導入

iOSアプリにはアプリを切り替える時に、最後に表示していた画面を「スナップショット」として保存しておく機能があります。
iPhoneを利用されている方ならご存知かと思いますが、アプリスイッチャーでアプリを切り替える時に、以前表示していた画面が見ることができます。

便利な機能なんですが、これによって個人情報などがスナップショットとして保存され、セキュリティ的な観点では問題になるとのこと。

こんな感じでアプリがバックグラウンド状態にある時に、名前やメアドなどの個人情報がスナップショットとして取得されていることが分かります。
before.png

そこで今回は、個人情報をOSのスナップショットから守る実装を書いてみました。

実装方法

結論から書くと
「バックグラウンドに入った時に個人情報を含むオブジェクトをisHidden=trueに設定し、アクティブになったら再表示する」という処理を書くだけです。
セキュアなアプリを実現すると言うことで、難しい事をするように感じるかもしれませんがシンプルです。

バックグラウンドでもセキュアなアプリを実装した場合のキャプチャ

after.png

上記キャプチャをみてお分かりだと思いますが、1枚目のキャプチャと違って、アプリが非アクティブのときに、個人情報に関するスナップショットが取得されていないことが分かります。

ソースコード

こんな感じです。サンプルアプリは非常にシンプルな実装ですが、アプリが複雑になっても基本的な実装方法は変わらないです。

import UIKit

// 個人情報を保持する画面にはSecretScreenProtocolを準拠させる
protocol SecretScreenProtocol: AnyObject {
    func protectSecretObject()
    func displaySecretObject()
}

class ViewController: UIViewController {

    @IBOutlet weak var labelName: UILabel!
    @IBOutlet weak var labelAddress: UILabel!

    let notificationCenter = NotificationCenter.default

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        print("Add Observer")
        notificationCenter.addObserver(self,
                                       selector: #selector(protectSecretObject),
                                       name: UIApplication.willResignActiveNotification,
                                       object: nil)
        notificationCenter.addObserver(self,
                                       selector: #selector(displaySecretObject),
                                       name: UIApplication.didBecomeActiveNotification,
                                       object: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        print("Remove Observer")
        notificationCenter.removeObserver(self,
                                          name: UIApplication.willResignActiveNotification,
                                          object: nil)
        notificationCenter.removeObserver(self,
                                          name: UIApplication.didBecomeActiveNotification,
                                          object: nil)
    }
}

// MARK: - SecretScreenProtocol
extension ViewController: SecretScreenProtocol {
    @objc func protectSecretObject() {
        print("Protect Secret Object")
        // アプリが非アクティブになる時に、個人情報を非表示にする
        labelName.isHidden = true
        labelAddress.isHidden = true
    }

    @objc func displaySecretObject() {
        print("Display Secret Object")
        // アプリがアクティブになる時に、個人情報を再表示する
        labelName.isHidden = false
        labelAddress.isHidden = false
    }
}

解説

ポイントは2つあります。
1. NotificationCenterを使ってアプリの状態を判断する
2. 個人情報を扱うViewControllerにはセキュリティを強化するためのプロトコルを準拠させる

1. NotificationCenterを使ってアプリの状態を判断する

NotificationCenterを活用することで、アプリがバックグラウンドから復帰したときや、アクティブでなくなる瞬間を検知し、表示している画面に対して、個別に処理を実装することができます。

使い方としては、
ViewDidLoad()viewWillAppear()などが呼ばれるタイミングで、addObserverして、画面に対して通知設定を追加します。
selectorの中に、通知が来た場合に実施したい処理を書いたメソッドを指定します。
nameの中にどういったタイミングで通知されたいかを指定します。

逆に画面から離れたらその通知は不要なのでviewWillDisappear()のタイミングでremoveObserverをして通知設定を解除します。
removeObserverは、不要になった!という意見もあるようでしたが、僕の場合removeObserverを設定しなかったことにより、通知設定が解除されないバグが発生したため、今回は追加しています。詳しい情報をお持ちの方がいたらコメント欄にて教えていただけると嬉しいです。

今回は、アプリがアクティブではなくなる時とアクティブに戻るときを判断したいので、2種類のNotificationCenterを活用します。

UIApplication 概要 今回行う処理
willResignActiveNotification アプリがアクティブでなくなった時に通知される 個人情報を隠す
didBecomeActiveNotification アプリがアクティブになった時に通知される 個人情報を表示する
2. 個人情報を扱うViewControllerにはセキュリティを強化するためのプロトコルを準拠させる

結論から申し上げますと、この処理は必須ではありません。1番で挙げた処理だけを行えば、普通に動きます。
でもプロトコルに準拠させて、実装ミスを減らしたり、実装を統一することができることがSwiftの強みだと思うので、今回はプロトコルを使ってみました。

今回の例でいうとSecretScreenProtocolをViewControllerに準拠させることで、強制的にセキュリティを強化するメソッドを実装するようにしています。

参考にさせていただいた記事・サイト

https://dev.to/esmeraldibejolli/mobile-security-in-background-app-snapshots-1lmn
https://qiita.com/tosh_3/items/df52802514cc9737e75b

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter bundle IDはどこで確認できる?

Flutterアプリ、bundle IDはどこで確認する?

アプリ初心者にとっては「bundle ID」がそもそも何なのかわからなかったりする。。
※AndroidではPackage nameと呼ばれてたりもします!

Flutterはプロジェクトを作成した時点で、「デフォルトのbundle ID」が作成されているので、それを確認してみましょう!!

また、bundle IDを変更する場合も同様の場所からできます。

Android

android/app/build.gradleで確認できます。

※build.gradleはandroid/build.gradleにも存在するので注意!
スクリーンショット 2021-03-04 15.29.35.jpg

// 省略
 defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.myApp" // これがAndroidのbundle ID
        minSdkVersion 16
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
// 省略

iOS

Xcodeでios/Runner.xcodeprojを開いて確認します。
スクリーンショット 2021-03-04 15.39.44.jpg

RunnerをクリックしてGeneralの項目で確認できます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[iOS]Firebaseメールリンク認証を実装してみた

目次

はじめに
環境
メールリンク認証とは
本記事で作成するサンプル
実装手順
1. FirebaseをiOSプロジェクトに追加 (省略)
2. Firebaseプロジェクトでメールリンクログインを有効にする
3. Firebase Dynamic Linksを構成する
4. メールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する
最後に
参考文献

はじめに

Firebase Authenticationを使用すれば、アプリでの様々なユーザー認証 (例えば、パスワード、電話番号、Googleアカウントなど)を比較的容易に実装することができます。
本記事では、「メールリンク認証 (iOS)」についての実装手順をまとめてみました。

環境

[Firebase SDK (iOS)] Version 7.7.0
[Xcode] Version 12.4
[Swift] Version 5.3.2
[iOS] 14.4
[MacOS] 10.15.7

メールリンク認証とは

メールリンク認証とは、下図のようなログイン用のリンクを含むメールをユーザーに送信し、そのリンクからログインしてもらう認証方法です。
1.PNG
メールリンク認証にはいくつもメリットがあるので以下に示します。
- 登録とログインが簡単
- ユーザーがメールアドレスの正当な所有者であることを確認できる
- パスワード不要 (覚える必要なし)で、安全にログインできる
などです。

本記事で作成するサンプル

今回は、ユーザーのメールアドレスを受け取り、送信されたメールのリンクからアプリを起動してログインするというシンプルなアプリを想定します。
2.PNG

実装手順

Firebase公式ドキュメントに従い、以下の流れで実装していきます。
1. FirebaseをiOSプロジェクトに追加 (本記事では省略)
2. Firebaseプロジェクトでメールリンクログインを有効にする
3. Dynamic Linksを構成する
4. ユーザーのメールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する

1. FirebaseをiOSプロジェクトに追加 (省略)

本記事では省略します。(公式ドキュメント 「FirebaseをiOSプロジェクトに追加する」を参照してください)
Firebase SDKの追加については、Podfileファイルには以下のPodを含めます。

Podfile
  pod 'Firebase/Analytics'
  pod 'Firebase/Auth'
  pod 'Firebase/DynamicLinks'

2. Firebaseプロジェクトでメールリンクログインを有効にする

Firebase コンソールで[Authentication]セクションを開き、[始める]を選択します。
3.png  4.png
[Sign-in method]タブで[メール/パスワード]を編集します。
5.png
[メール/パスワード]と[メールリンク]の両方を有効にして保存します。
6.png
これでメールリンクが有効となりました。

3. Firebase Dynamic Linksを構成する

3-1. Firebase Dynamic Linksとは

Firebase Dynamic Links とは、アプリのインストールの有無に関わらす、複数のプラットフォームで機能するディープリンクです。
iOS または Android でダイナミックリンクを開くと、ネイティブアプリのリンク先のコンテンツに直接移動できます。
デスクトップ ブラウザでダイナミックリンクを開くと、ウェブサイト上の同じコンテンツに移動できます。
また、ダイナミックリンクはアプリのインストールが必要な場面でも利用できます。たとえば、ダイナミックリンクを開いたユーザーの iOS デバイスまたは Android デバイスにアプリがインストールされていない場合にインストールを促し、インストール完了後にアプリを起動してリンクを開くといったこともできます。

Firebase Dynamic Linksより引用

3-2. App Store IDとチームIDを取得する

ダイナミックリンクを作成する際に、「App Store ID」と「チームID」が必要となるので先に取得しておきます。
すでに取得済みの方はスキップしてください。

チームIDの取得

Apple Developerアカウントの[Certifications, IDs & Profiles]セクションの[Identifiers]セクションで確認できます。App ID PrefixがチームIDです。
アプリを未登録の方は、以下の手順で登録できます。
[Certifications, IDs & Profiles]セクションをクリックします。
7.png
[Identifiers]セクションのボタンをクリックします。
8.png
App IDs」を選択して「Continue」で次に進みます。
9.png
Select a type」で「App」を選択して「Continue」で次に進みます。
10.png
Description」にはアプリの簡単な説明を入力します。アプリ名でも良さそうです。
Bundle ID」にはXcodeプロジェクトのBundle Identifierを正確に入力してください。
Capabolotoes」では、アプリリリース時にはアプリの機能を漏れなく選択する必要がありますが、今回は最低限必要な「Associated Domains」を選択して次に進みます。
11.png
App ID Prefix」がチームIDです。これでチームIDの確認を完了しました。
12.png

App Store IDの取得

App Store Connectの「マイApp」から自分の登録しているアプリをクリックして、[App情報]セクションにあるApple IDがApp Store IDです。
アプリを未登録の方は以下の手順で登録できます。
マイApp」をクリックします。
13.png
作成しているアプリを「新規App」から追加します。
14.png
アプリの名前」、「使用する主な言語」、「Bundle ID」、「SKU」を設定します。
SKUはアプリを区別できる名前であれば何でも良いと思います。
今回はアプリ名にしました。
15.png
作成を終えたら、[App情報]セクションをクリックしてApple IDを確認します。これがApp Store IDに当たるので、確認完了です。
16.png

3-3. Dynamic Linksを作成する

公式ドキュメントによると、Dynamic Linksの作成方法は4つありますが、今回はFirebase コンソールで作成します。
[Dynamic Links]セクションを開き、「始める」をクリックします。
17.png  18.png
Dynamic LinksのURLドメインを作成します。
ドメインは特に指定しなければGoogle提供の.page.linkが与えられるので、それを使用します。
サブドメインはアプリ名などの分かりやすいものが良いです。(例えば、今回ならURLをopenapp.page.linkにするとか)
19.png20.png
次に、「新しいダイナミック リンク」ボタンからダイナミックリンクを作成します。
21.png
短縮URLのリンク設定では、デフォルト値でも問題ありません。今回は分かりやすいように「open」としています。
22.png
次に、ダイナミックリンクの設定です。
ディープリンクURLによって特定のコンテンツに直接遷移することが可能となります。ディープリンクは、オリジナルのURLで良いようなので遷移先が分かるように設定します。
今回は、アプリ自身に移動するので、[Authentication]セクションの承認済みドメインURLをコピーして使用しています。
ダイナミックリンク名」は処理が分かりやすいように、「Open App」のようにします。
23.png
24.png
次に、iOSデバイスでリンクを開いたときの動作を設定します。
今回は「ディープリンクでiOSアプリを開く」を選択して、アプリを指定します。
このとき、Firebase コンソールの[プロジェクト設定]セクションにおいて、App Store IDチームIDを設定している必要があります。
設定していない場合、アプリを指定しようとすると、「App Store IDとチームIDをここに追加してください」という警告が表示されるので、未設定の方は入力してください。
25.png
26.png
次に、アプリがインストールされていないときの動作を設定します。
今回はApp Storeに移動します。
28.png
Androidでの動作設定や、その他詳細オプションを設定できますが、今回は特に何も設定をせずに進めます。
29.png  30.png
以上で、ダイナミックリンクの作成は完了です。
リンクURLをコピーして、[Authentication]セクションの[Sign-in method]タブ内にある「承認済みドメイン」に、ダイナミックリンクURLのドメインを追加します。
31.png
32.png
下記のように、追加したURLドメインが一覧に表示されていればOKです。
33.png
以上で、コンソールにおけるDyanmic Linksの構成は完了です。
次にXcodeプロジェクトの設定を行います。

3-4. Xcodeプロジェクトの設定

TARGETS」の[Signing & Capabilities]タブにある「+Capability」をクリックして「Associated Domains」を追加します。
34.png
Domains」に「applinks:example.page.link」を入力してください。「example.page.link」はFirebaseコンソールで作成したダイナミックリンクURLのドメインです。
35.png
次に、同じく「TARGETS」の[Info]タブで、「URL Types」を開いて+をクリックし、「Identifier」と「URL Schemes」を入力します。
Identifier」にはアプリ名を入力しておきます。
URL Schemes」にはアプリのBundle Identifierを入力します。
36.png
以上でXcodeプロジェクトでの設定も完了です。

4. メールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する

4-1. ActionCodeSettingsオブジェクトを作成する

メールリンクの作成方法をFirebaseに伝えるActionCodeSettingsオブジェクトを作成します。

SendEmailViewController
    //・・・
    import Firebase

    @IBOutlet weak var emailTextField: UITextField! //ユーザーからメールアドレスを受け取るUIを準備しておきます

    //・・・

    let email = emailTextField.text ?? "" //ユーザーのメールアドレス

    let actionCodeSettings = ActionCodeSettings() //メールリンクの作成方法をFirebaseに伝えるオブジェクト
    actionCodeSettings.handleCodeInApp = true //ログインをアプリ内で完結させる必要があります
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!) //iOSデバイス内でログインリンクを開くアプリのBundle ID
    //リンクURL
    var components = URLComponents()
    components.scheme = "https"
    components.host = "maillinkdemo.page.link" //Firebaseコンソールで作成したダイナミックリンクURLドメイン
    let queryItemEmailName = "email" //URLにemail情報(パラメータ)を追加する
    let emailTypeQueryItem = URLQueryItem(name: queryItemEmailName, value: email)
    components.queryItems = [emailTypeQueryItem]
    guard let linkParameter = components.url else { return }
    print("email: \(linkParameter.absoluteString)")
    actionCodeSettings.url = linkParameter  
    //actionCodeSettingsの作成完了

    //・・・

4-2. メールアドレスに認証リンクを送信する

ユーザーのメールアドレスemailと作成したactionCodeSettingsを使用して、ユーザーのメールアドレスに認証リンクを送信します。
ログインメールの送信時に、メールアドレスをローカルに保存しておきます。
Firebase Auth`では、認証フロー完了時にユーザーのメールアドレスとログインリンクの送信アドレスが一致するかどうかを確認するため、ローカルに保存しておくことで、ユーザーに再度メールアドレスを入力してもらわなくても認証フローを完了できます。

メールリンクログインを完了する | iOS でメールリンクを使用して Firebase 認証を行う

SendEmailViewController
    //・・・

    //ユーザーのメールアドレスに認証リンクを送信
    Auth.auth().sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { (err) in
        if let err = err {
            print("送信失敗")
            print(err.localizedDescription)
            return
        }
        print("送信完了")

        //ローカルにメールアドレスを保存しておく
        UserDefaults.standard.set(email, forKey: "email")


        //・・・
        //アラートを表示するなど、ユーザーにメールの確認を促す処理

    }

4-3. リンクを確認して、ログインを完了する

リンクを受信したら、それがメールリンク認証用であることを確認してログインを完了します。
ユーザーがメールに含まれたリンクをタップすると、次のようなページに遷移します。
37.PNG
OPEN」ボタンをタップするとアプリが開きます。 (アプリがインストールされていない場合は、AppStoreを開きます)
アプリを開くと、SceneDelegateクラスのscene(_:continue:)メソッドが呼ばれるので、この中にログイン完了の処理を書きます。今回は、サインインしたら画面を切り替えるというシンプルな処理にしています。
公式ドキュメントには、AppDelegateクラスのapplication(_:continue:restorationHandler:)メソッドの中で処理を書いていますが、iOS13以降ではダイナミックリンクからアプリを開いたときにapplication(_:continue:restorationHandler:)メソッドに処理を書いても機能しないようです。
下記の記事が分かりやすく、参考にさせていただきました。
参考: [iOS13] SceneDelegateでDynamicLinksからのURLをハンドリングする

SceneDelegate
import Firebase

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    //・・・

    var window: UIWindow?

    //ユーザーのアクティビティオブジェクトの情報を全て受信したときに呼ばれる
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {

        //userActivityプロパティからリンクURLを取得
        guard let url = userActivity.webpageURL else { return }
        let link = url.absoluteString

        if Auth.auth().isSignIn(withEmailLink: link) {
            //ローカルに保存していたメールアドレスを取得
            guard let email = UserDefaults.standard.value(forKey: "email") as? String else {
                print("メールアドレスが存在しません")
                return
            }
            //ログイン処理
            Auth.auth().signIn(withEmail: email, link: link) { (auth, err) in
                if let err = err {
                    print(err.localizedDescription)
                    print("ログイン失敗")
                    return
                }

                print("ログイン成功")

                //ログイン成功時の処理 (例えば、今回は画面切り替えなどの処理)
                guard let scene = (scene as? UIWindowScene) else { return }
                let window = UIWindow(windowScene: scene)
                self.window = window
                window.makeKeyAndVisible()
                let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
                let didSignInVC = storyboard.instantiateViewController(withIdentifier: "DidSignInViewController")
                self.window?.rootViewController = viewController
            }
        }
    }

    //・・・
}

4-4. [補足] サインアウトする

サインアウト処理は以下のように簡単に書けます。

DidSignViewController
import UIKit
import Firebase

class DidSignInViewController: UIViewController {

    //・・・

    //サインアウトボタンを準備しておく
    @IBAction func signButtonTapped(_ sender: UIButton) {
        //ログイン状態ならば、サインアウト処理を行う
        if let user = Auth.auth().currentUser {            
            do {
                try Auth.auth().signOut()
                print("サインアウト完了")
                //画面切り替えなど、サインアウト後の処理
                //・・・

            } catch let signOutError as NSError {
                print(signOutError.localizedDescription)
                return
            }
        }
    }
}

最後に

Firebaseを利用したメールリンク認証の実装手順をまとめてみました。
主観ですが、パスワードを用いたログインに比べてユーザー視点でのメリットが多いと感じました。
今回実装してみて、ダイナミックリンクはFirebaseコンソールで簡単に作成できるものの、細かい設定などに少し手こずりました。
「参考文献」に記載しましたが、iOSでのFirebase Dynamic Linksの実装について分かりやすい記事をいくつも参考にさせていただきました。
しかし、iOSアップデートなどにより、そのまま手順通りにやっても上手くいかない箇所もあるため、合わせてこの記事がほんの少しでも役に立てば幸いです。
初めて記事を投稿したため、分かりづらい点が多いと思いますが、今後もアップデートしていければと思います。

参考文献

この記事は以下の情報を参考にしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[iOS] Firebaseメールリンク認証を実装してみた

目次

はじめに
環境
メールリンク認証とは
本記事で作成するサンプル
実装手順
1. FirebaseをiOSプロジェクトに追加 (省略)
2. Firebaseプロジェクトでメールリンクログインを有効にする
3. Firebase Dynamic Linksを構成する
4. メールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する
最後に
参考文献

はじめに

Firebase Authenticationを使用すれば、アプリでの様々なユーザー認証 (例えば、パスワード、電話番号、Googleアカウントなど)を比較的容易に実装することができます。
本記事では、「メールリンク認証 (iOS)」についての実装手順をまとめてみました。

環境

[Firebase SDK (iOS)] Version 7.7.0
[Xcode] Version 12.4
[Swift] Version 5.3.2
[iOS] 14.4
[MacOS] 10.15.7

メールリンク認証とは

メールリンク認証とは、下図のようなログイン用のリンクを含むメールをユーザーに送信し、そのリンクからログインしてもらう認証方法です。
1.PNG
メールリンク認証にはいくつもメリットがあるので以下に示します。
- 登録とログインが簡単
- ユーザーがメールアドレスの正当な所有者であることを確認できる
- パスワード不要 (覚える必要なし)で、安全にログインできる
などです。

本記事で作成するサンプル

今回は、ユーザーのメールアドレスを受け取り、送信されたメールのリンクからアプリを起動してログインするというシンプルなアプリを想定します。
2.PNG

実装手順

Firebase公式ドキュメントに従い、以下の流れで実装していきます。
1. FirebaseをiOSプロジェクトに追加 (本記事では省略)
2. Firebaseプロジェクトでメールリンクログインを有効にする
3. Dynamic Linksを構成する
4. ユーザーのメールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する

1. FirebaseをiOSプロジェクトに追加 (省略)

本記事では省略します。(公式ドキュメント 「FirebaseをiOSプロジェクトに追加する」を参照してください)
Firebase SDKの追加については、Podfileファイルには以下のPodを含めます。

Podfile
  pod 'Firebase/Analytics'
  pod 'Firebase/Auth'
  pod 'Firebase/DynamicLinks'

2. Firebaseプロジェクトでメールリンクログインを有効にする

Firebase コンソールで[Authentication]セクションを開き、[始める]を選択します。
3.png  4.png
[Sign-in method]タブで[メール/パスワード]を編集します。
5.png
[メール/パスワード]と[メールリンク]の両方を有効にして保存します。
6.png
これでメールリンクが有効となりました。

3. Firebase Dynamic Linksを構成する

3-1. Firebase Dynamic Linksとは

Firebase Dynamic Links とは、アプリのインストールの有無に関わらす、複数のプラットフォームで機能するディープリンクです。
iOS または Android でダイナミックリンクを開くと、ネイティブアプリのリンク先のコンテンツに直接移動できます。
デスクトップ ブラウザでダイナミックリンクを開くと、ウェブサイト上の同じコンテンツに移動できます。
また、ダイナミックリンクはアプリのインストールが必要な場面でも利用できます。たとえば、ダイナミックリンクを開いたユーザーの iOS デバイスまたは Android デバイスにアプリがインストールされていない場合にインストールを促し、インストール完了後にアプリを起動してリンクを開くといったこともできます。

Firebase Dynamic Linksより引用

3-2. App Store IDとチームIDを取得する

ダイナミックリンクを作成する際に、「App Store ID」と「チームID」が必要となるので先に取得しておきます。
すでに取得済みの方はスキップしてください。

チームIDの取得

Apple Developerアカウントの[Certifications, IDs & Profiles]セクションの[Identifiers]セクションで確認できます。App ID PrefixがチームIDです。
アプリを未登録の方は、以下の手順で登録できます。
[Certifications, IDs & Profiles]セクションをクリックします。
7.png
[Identifiers]セクションのボタンをクリックします。
8.png
App IDs」を選択して「Continue」で次に進みます。
9.png
Select a type」で「App」を選択して「Continue」で次に進みます。
10.png
Description」にはアプリの簡単な説明を入力します。アプリ名でも良さそうです。
Bundle ID」にはXcodeプロジェクトのBundle Identifierを正確に入力してください。
Capabolotoes」では、アプリリリース時にはアプリの機能を漏れなく選択する必要がありますが、今回は最低限必要な「Associated Domains」を選択して次に進みます。
11.png
App ID Prefix」がチームIDです。これでチームIDの確認を完了しました。
12.png

App Store IDの取得

App Store Connectの「マイApp」から自分の登録しているアプリをクリックして、[App情報]セクションにあるApple IDがApp Store IDです。
アプリを未登録の方は以下の手順で登録できます。
マイApp」をクリックします。
13.png
作成しているアプリを「新規App」から追加します。
14.png
アプリの名前」、「使用する主な言語」、「Bundle ID」、「SKU」を設定します。
SKUはアプリを区別できる名前であれば何でも良いと思います。
今回はアプリ名にしました。
15.png
作成を終えたら、[App情報]セクションをクリックしてApple IDを確認します。これがApp Store IDに当たるので、確認完了です。
16.png

3-3. Dynamic Linksを作成する

公式ドキュメントによると、Dynamic Linksの作成方法は4つありますが、今回はFirebase コンソールで作成します。
[Dynamic Links]セクションを開き、「始める」をクリックします。
17.png  18.png
Dynamic LinksのURLドメインを作成します。
ドメインは特に指定しなければGoogle提供の.page.linkが与えられるので、それを使用します。
サブドメインはアプリ名などの分かりやすいものが良いです。(例えば、今回ならURLをopenapp.page.linkにするとか)
19.png20.png
次に、「新しいダイナミック リンク」ボタンからダイナミックリンクを作成します。
21.png
短縮URLのリンク設定では、デフォルト値でも問題ありません。今回は分かりやすいように「open」としています。
22.png
次に、ダイナミックリンクの設定です。
ディープリンクURLによって特定のコンテンツに直接遷移することが可能となります。ディープリンクは、オリジナルのURLで良いようなので遷移先が分かるように設定します。
今回は、アプリ自身に移動するので、[Authentication]セクションの承認済みドメインURLをコピーして使用しています。
ダイナミックリンク名」は処理が分かりやすいように、「Open App」のようにします。
23.png
24.png
次に、iOSデバイスでリンクを開いたときの動作を設定します。
今回は「ディープリンクでiOSアプリを開く」を選択して、アプリを指定します。
このとき、Firebase コンソールの[プロジェクト設定]セクションにおいて、App Store IDチームIDを設定している必要があります。
設定していない場合、アプリを指定しようとすると、「App Store IDとチームIDをここに追加してください」という警告が表示されるので、未設定の方は入力してください。
25.png
26.png
次に、アプリがインストールされていないときの動作を設定します。
今回はApp Storeに移動します。
28.png
Androidでの動作設定や、その他詳細オプションを設定できますが、今回は特に何も設定をせずに進めます。
29.png  30.png
以上で、ダイナミックリンクの作成は完了です。
リンクURLをコピーして、[Authentication]セクションの[Sign-in method]タブ内にある「承認済みドメイン」に、ダイナミックリンクURLのドメインを追加します。
31.png
32.png
下記のように、追加したURLドメインが一覧に表示されていればOKです。
33.png
以上で、コンソールにおけるDyanmic Linksの構成は完了です。
次にXcodeプロジェクトの設定を行います。

3-4. Xcodeプロジェクトの設定

TARGETS」の[Signing & Capabilities]タブにある「+Capability」をクリックして「Associated Domains」を追加します。
34.png
Domains」に「applinks:example.page.link」を入力してください。「example.page.link」はFirebaseコンソールで作成したダイナミックリンクURLのドメインです。
35.png
次に、同じく「TARGETS」の[Info]タブで、「URL Types」を開いて+をクリックし、「Identifier」と「URL Schemes」を入力します。
Identifier」にはアプリ名を入力しておきます。
URL Schemes」にはアプリのBundle Identifierを入力します。
36.png
以上でXcodeプロジェクトでの設定も完了です。

4. メールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する

4-1. ActionCodeSettingsオブジェクトを作成する

メールリンクの作成方法をFirebaseに伝えるActionCodeSettingsオブジェクトを作成します。

SendEmailViewController
    //・・・
    import Firebase

    @IBOutlet weak var emailTextField: UITextField! //ユーザーからメールアドレスを受け取るUIを準備しておきます

    //・・・

    let email = emailTextField.text ?? "" //ユーザーのメールアドレス

    let actionCodeSettings = ActionCodeSettings() //メールリンクの作成方法をFirebaseに伝えるオブジェクト
    actionCodeSettings.handleCodeInApp = true //ログインをアプリ内で完結させる必要があります
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!) //iOSデバイス内でログインリンクを開くアプリのBundle ID
    //リンクURL
    var components = URLComponents()
    components.scheme = "https"
    components.host = "maillinkdemo.page.link" //Firebaseコンソールで作成したダイナミックリンクURLドメイン
    let queryItemEmailName = "email" //URLにemail情報(パラメータ)を追加する
    let emailTypeQueryItem = URLQueryItem(name: queryItemEmailName, value: email)
    components.queryItems = [emailTypeQueryItem]
    guard let linkParameter = components.url else { return }
    print("email: \(linkParameter.absoluteString)")
    actionCodeSettings.url = linkParameter  
    //actionCodeSettingsの作成完了

    //・・・

4-2. メールアドレスに認証リンクを送信する

ユーザーのメールアドレスemailと作成したactionCodeSettingsを使用して、ユーザーのメールアドレスに認証リンクを送信します。
ログインメールの送信時に、メールアドレスをローカルに保存しておきます。
Firebase Auth`では、認証フロー完了時にユーザーのメールアドレスとログインリンクの送信アドレスが一致するかどうかを確認するため、ローカルに保存しておくことで、ユーザーに再度メールアドレスを入力してもらわなくても認証フローを完了できます。

メールリンクログインを完了する | iOS でメールリンクを使用して Firebase 認証を行う

SendEmailViewController
    //・・・

    //ユーザーのメールアドレスに認証リンクを送信
    Auth.auth().sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { (err) in
        if let err = err {
            print("送信失敗")
            print(err.localizedDescription)
            return
        }
        print("送信完了")

        //ローカルにメールアドレスを保存しておく
        UserDefaults.standard.set(email, forKey: "email")


        //・・・
        //アラートを表示するなど、ユーザーにメールの確認を促す処理

    }

4-3. リンクを確認して、ログインを完了する

リンクを受信したら、それがメールリンク認証用であることを確認してログインを完了します。
ユーザーがメールに含まれたリンクをタップすると、次のようなページに遷移します。
37.PNG
OPEN」ボタンをタップするとアプリが開きます。 (アプリがインストールされていない場合は、AppStoreを開きます)
アプリを開くと、SceneDelegateクラスのscene(_:continue:)メソッドが呼ばれるので、この中にログイン完了の処理を書きます。今回は、サインインしたら画面を切り替えるというシンプルな処理にしています。
公式ドキュメントには、AppDelegateクラスのapplication(_:continue:restorationHandler:)メソッドの中で処理を書いていますが、iOS13以降ではダイナミックリンクからアプリを開いたときにapplication(_:continue:restorationHandler:)メソッドに処理を書いても機能しないようです。
下記の記事が分かりやすく、参考にさせていただきました。
参考: [iOS13] SceneDelegateでDynamicLinksからのURLをハンドリングする

SceneDelegate
import Firebase

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    //・・・

    var window: UIWindow?

    //ユーザーのアクティビティオブジェクトの情報を全て受信したときに呼ばれる
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {

        //userActivityプロパティからリンクURLを取得
        guard let url = userActivity.webpageURL else { return }
        let link = url.absoluteString

        if Auth.auth().isSignIn(withEmailLink: link) {
            //ローカルに保存していたメールアドレスを取得
            guard let email = UserDefaults.standard.value(forKey: "email") as? String else {
                print("メールアドレスが存在しません")
                return
            }
            //ログイン処理
            Auth.auth().signIn(withEmail: email, link: link) { (auth, err) in
                if let err = err {
                    print(err.localizedDescription)
                    print("ログイン失敗")
                    return
                }

                print("ログイン成功")

                //ログイン成功時の処理 (例えば、今回は画面切り替えなどの処理)
                guard let scene = (scene as? UIWindowScene) else { return }
                let window = UIWindow(windowScene: scene)
                self.window = window
                window.makeKeyAndVisible()
                let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
                let didSignInVC = storyboard.instantiateViewController(withIdentifier: "DidSignInViewController")
                self.window?.rootViewController = viewController
            }
        }
    }

    //・・・
}

4-4. [補足] サインアウトする

サインアウト処理は以下のように簡単に書けます。

DidSignViewController
import UIKit
import Firebase

class DidSignInViewController: UIViewController {

    //・・・

    //サインアウトボタンを準備しておく
    @IBAction func signButtonTapped(_ sender: UIButton) {
        //ログイン状態ならば、サインアウト処理を行う
        if let user = Auth.auth().currentUser {            
            do {
                try Auth.auth().signOut()
                print("サインアウト完了")
                //画面切り替えなど、サインアウト後の処理
                //・・・

            } catch let signOutError as NSError {
                print(signOutError.localizedDescription)
                return
            }
        }
    }
}

最後に

Firebaseを利用したメールリンク認証の実装手順をまとめてみました。
主観ですが、パスワードを用いたログインに比べてユーザー視点でのメリットが多いと感じました。
今回実装してみて、ダイナミックリンクはFirebaseコンソールで簡単に作成できるものの、細かい設定などに少し手こずりました。
「参考文献」に記載しましたが、iOSでのFirebase Dynamic Linksの実装について分かりやすい記事をいくつも参考にさせていただきました。
しかし、iOSアップデートなどにより、そのまま手順通りにやっても上手くいかない箇所もあるため、合わせてこの記事がほんの少しでも役に立てば幸いです。
初めて記事を投稿したため、分かりづらい点が多いと思いますが、今後もアップデートしていければと思います。

参考文献

この記事は以下の情報を参考にしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む