- 投稿日:2021-03-04T22:58:57+09:00
【Swift】バックグラウンド時にもセキュアなiOSアプリを実装する
導入
iOSアプリにはアプリを切り替える時に、最後に表示していた画面を「スナップショット」として保存しておく機能があります。
iPhoneを利用されている方ならご存知かと思いますが、アプリスイッチャーでアプリを切り替える時に、以前表示していた画面が見ることができます。便利な機能なんですが、これによって個人情報などがスナップショットとして保存され、セキュリティ的な観点では問題になるとのこと。
こんな感じでアプリがバックグラウンド状態にある時に、名前やメアドなどの個人情報がスナップショットとして取得されていることが分かります。
そこで今回は、個人情報をOSのスナップショットから守る実装を書いてみました。
実装方法
結論から書くと
「バックグラウンドに入った時に個人情報を含むオブジェクトをisHidden=trueに設定し、アクティブになったら再表示する」という処理を書くだけです。
セキュアなアプリを実現すると言うことで、難しい事をするように感じるかもしれませんがシンプルです。バックグラウンドでもセキュアなアプリを実装した場合のキャプチャ
上記キャプチャをみてお分かりだと思いますが、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
- 投稿日:2021-03-04T16:28:11+09:00
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
にも存在するので注意!
// 省略 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
を開いて確認します。
Runner
をクリックしてGeneral
の項目で確認できます。
- 投稿日:2021-03-04T01:49:12+09:00
[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メールリンク認証とは
メールリンク認証とは、下図のようなログイン用のリンクを含むメールをユーザーに送信し、そのリンクからログインしてもらう認証方法です。
メールリンク認証にはいくつもメリットがあるので以下に示します。
- 登録とログインが簡単
- ユーザーがメールアドレスの正当な所有者であることを確認できる
- パスワード不要 (覚える必要なし)で、安全にログインできる
などです。本記事で作成するサンプル
今回は、ユーザーのメールアドレスを受け取り、送信されたメールのリンクからアプリを起動してログインするというシンプルなアプリを想定します。
実装手順
Firebase公式ドキュメントに従い、以下の流れで実装していきます。
1. FirebaseをiOSプロジェクトに追加 (本記事では省略)
2. Firebaseプロジェクトでメールリンクログインを有効にする
3. Dynamic Linksを構成する
4. ユーザーのメールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する1. FirebaseをiOSプロジェクトに追加 (省略)
本記事では省略します。(公式ドキュメント 「FirebaseをiOSプロジェクトに追加する」を参照してください)
Firebase SDKの追加については、Podfile
ファイルには以下のPod
を含めます。Podfilepod 'Firebase/Analytics' pod 'Firebase/Auth' pod 'Firebase/DynamicLinks'2. Firebaseプロジェクトでメールリンクログインを有効にする
Firebase コンソールで[
Authentication
]セクションを開き、[始める
]を選択します。
[Sign-in method
]タブで[メール/パスワード
]を編集します。
[メール/パスワード
]と[メールリンク
]の両方を有効にして保存します。
これでメールリンクが有効となりました。3. Firebase Dynamic Linksを構成する
3-1. Firebase Dynamic Linksとは
Firebase Dynamic Links
とは、アプリのインストールの有無に関わらす、複数のプラットフォームで機能するディープリンクです。
iOS または Android でダイナミックリンクを開くと、ネイティブアプリのリンク先のコンテンツに直接移動できます。
デスクトップ ブラウザでダイナミックリンクを開くと、ウェブサイト上の同じコンテンツに移動できます。
また、ダイナミックリンクはアプリのインストールが必要な場面でも利用できます。たとえば、ダイナミックリンクを開いたユーザーの iOS デバイスまたは Android デバイスにアプリがインストールされていない場合にインストールを促し、インストール完了後にアプリを起動してリンクを開くといったこともできます。3-2. App Store IDとチームIDを取得する
ダイナミックリンクを作成する際に、「
App Store ID
」と「チームID
」が必要となるので先に取得しておきます。
すでに取得済みの方はスキップしてください。チームIDの取得
Apple Developerアカウントの[
Certifications, IDs & Profiles
]セクションの[Identifiers
]セクションで確認できます。「App ID Prefix
」がチームIDです。
アプリを未登録の方は、以下の手順で登録できます。
[Certifications, IDs & Profiles
]セクションをクリックします。
[Identifiers
]セクションの+
ボタンをクリックします。
「App IDs
」を選択して「Continue
」で次に進みます。
「Select a type
」で「App
」を選択して「Continue
」で次に進みます。
「Description
」にはアプリの簡単な説明を入力します。アプリ名でも良さそうです。
「Bundle ID
」にはXcodeプロジェクトのBundle Identifierを正確に入力してください。
「Capabolotoes
」では、アプリリリース時にはアプリの機能を漏れなく選択する必要がありますが、今回は最低限必要な「Associated Domains
」を選択して次に進みます。
「App ID Prefix
」がチームIDです。これでチームIDの確認を完了しました。
App Store IDの取得
App Store Connectの「
マイApp
」から自分の登録しているアプリをクリックして、[App情報
]セクションにある「Apple ID
」がApp Store IDです。
アプリを未登録の方は以下の手順で登録できます。
「マイApp
」をクリックします。
作成しているアプリを「新規App
」から追加します。
「アプリの名前
」、「使用する主な言語
」、「Bundle ID
」、「SKU
」を設定します。
SKUはアプリを区別できる名前であれば何でも良いと思います。
今回はアプリ名にしました。
作成を終えたら、[App情報
]セクションをクリックして「Apple ID
」を確認します。これがApp Store IDに当たるので、確認完了です。
3-3. Dynamic Linksを作成する
公式ドキュメントによると、Dynamic Linksの作成方法は4つありますが、今回はFirebase コンソールで作成します。
[Dynamic Links
]セクションを開き、「始める
」をクリックします。
Dynamic LinksのURLドメインを作成します。
ドメインは特に指定しなければGoogle提供の.page.link
が与えられるので、それを使用します。
サブドメインはアプリ名などの分かりやすいものが良いです。(例えば、今回ならURLをopenapp.page.link
にするとか)
次に、「新しいダイナミック リンク
」ボタンからダイナミックリンクを作成します。
短縮URLのリンク設定では、デフォルト値でも問題ありません。今回は分かりやすいように「open」としています。
次に、ダイナミックリンクの設定です。
ディープリンクURLによって特定のコンテンツに直接遷移することが可能となります。ディープリンクは、オリジナルのURLで良いようなので遷移先が分かるように設定します。
今回は、アプリ自身に移動するので、[Authentication
]セクションの承認済みドメインURL
をコピーして使用しています。
「ダイナミックリンク名
」は処理が分かりやすいように、「Open App」のようにします。
次に、iOSデバイスでリンクを開いたときの動作を設定します。
今回は「ディープリンクでiOSアプリを開く
」を選択して、アプリを指定します。
このとき、Firebase コンソールの[プロジェクト設定
]セクションにおいて、App Store ID
とチームID
を設定している必要があります。
設定していない場合、アプリを指定しようとすると、「App Store IDとチームIDをここに追加してください
」という警告が表示されるので、未設定の方は入力してください。
次に、アプリがインストールされていないときの動作を設定します。
今回はApp Storeに移動します。
Androidでの動作設定や、その他詳細オプションを設定できますが、今回は特に何も設定をせずに進めます。
以上で、ダイナミックリンクの作成は完了です。
リンクURLをコピーして、[Authentication
]セクションの[Sign-in method
]タブ内にある「承認済みドメイン
」に、ダイナミックリンクURLのドメインを追加します。
下記のように、追加したURLドメインが一覧に表示されていればOKです。
以上で、コンソールにおけるDyanmic Linksの構成は完了です。
次にXcodeプロジェクトの設定を行います。3-4. Xcodeプロジェクトの設定
「
TARGETS
」の[Signing & Capabilities
]タブにある「+Capability
」をクリックして「Associated Domains
」を追加します。
「Domains
」に「applinks:example.page.link
」を入力してください。「example.page.link
」はFirebaseコンソールで作成したダイナミックリンクURLのドメインです。
次に、同じく「TARGETS
」の[Info
]タブで、「URL Types
」を開いて+
をクリックし、「Identifier
」と「URL Schemes
」を入力します。
「Identifier
」にはアプリ名を入力しておきます。
「URL Schemes
」にはアプリのBundle Identifierを入力します。
以上で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. メールアドレスに認証リンクを送信する
ユーザーのメールアドレス
actionCodeSettings
を使用して、ユーザーのメールアドレスに認証リンクを送信します。
ログインメールの送信時に、メールアドレスをローカルに保存しておきます。
Firebase Auth`では、認証フロー完了時にユーザーのメールアドレスとログインリンクの送信アドレスが一致するかどうかを確認するため、ローカルに保存しておくことで、ユーザーに再度メールアドレスを入力してもらわなくても認証フローを完了できます。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. リンクを確認して、ログインを完了する
リンクを受信したら、それがメールリンク認証用であることを確認してログインを完了します。
ユーザーがメールに含まれたリンクをタップすると、次のようなページに遷移します。
「OPEN
」ボタンをタップするとアプリが開きます。 (アプリがインストールされていない場合は、AppStoreを開きます)
アプリを開くと、SceneDelegate
クラスのscene(_:continue:)
メソッドが呼ばれるので、この中にログイン完了の処理を書きます。今回は、サインインしたら画面を切り替えるというシンプルな処理にしています。
公式ドキュメントには、AppDelegate
クラスのapplication(_:continue:restorationHandler:)
メソッドの中で処理を書いていますが、iOS13以降ではダイナミックリンクからアプリを開いたときにapplication(_:continue:restorationHandler:)
メソッドに処理を書いても機能しないようです。
下記の記事が分かりやすく、参考にさせていただきました。
参考: [iOS13] SceneDelegateでDynamicLinksからのURLをハンドリングするSceneDelegateimport 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. [補足] サインアウトする
サインアウト処理は以下のように簡単に書けます。
DidSignViewControllerimport 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アップデートなどにより、そのまま手順通りにやっても上手くいかない箇所もあるため、合わせてこの記事がほんの少しでも役に立てば幸いです。
初めて記事を投稿したため、分かりづらい点が多いと思いますが、今後もアップデートしていければと思います。参考文献
この記事は以下の情報を参考にしました。
- 投稿日:2021-03-04T01:49:12+09:00
[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メールリンク認証とは
メールリンク認証とは、下図のようなログイン用のリンクを含むメールをユーザーに送信し、そのリンクからログインしてもらう認証方法です。
メールリンク認証にはいくつもメリットがあるので以下に示します。
- 登録とログインが簡単
- ユーザーがメールアドレスの正当な所有者であることを確認できる
- パスワード不要 (覚える必要なし)で、安全にログインできる
などです。本記事で作成するサンプル
今回は、ユーザーのメールアドレスを受け取り、送信されたメールのリンクからアプリを起動してログインするというシンプルなアプリを想定します。
実装手順
Firebase公式ドキュメントに従い、以下の流れで実装していきます。
1. FirebaseをiOSプロジェクトに追加 (本記事では省略)
2. Firebaseプロジェクトでメールリンクログインを有効にする
3. Dynamic Linksを構成する
4. ユーザーのメールアドレスに認証リンクを送信して、アプリでユーザーのログインを完了する1. FirebaseをiOSプロジェクトに追加 (省略)
本記事では省略します。(公式ドキュメント 「FirebaseをiOSプロジェクトに追加する」を参照してください)
Firebase SDKの追加については、Podfile
ファイルには以下のPod
を含めます。Podfilepod 'Firebase/Analytics' pod 'Firebase/Auth' pod 'Firebase/DynamicLinks'2. Firebaseプロジェクトでメールリンクログインを有効にする
Firebase コンソールで[
Authentication
]セクションを開き、[始める
]を選択します。
[Sign-in method
]タブで[メール/パスワード
]を編集します。
[メール/パスワード
]と[メールリンク
]の両方を有効にして保存します。
これでメールリンクが有効となりました。3. Firebase Dynamic Linksを構成する
3-1. Firebase Dynamic Linksとは
Firebase Dynamic Links
とは、アプリのインストールの有無に関わらす、複数のプラットフォームで機能するディープリンクです。
iOS または Android でダイナミックリンクを開くと、ネイティブアプリのリンク先のコンテンツに直接移動できます。
デスクトップ ブラウザでダイナミックリンクを開くと、ウェブサイト上の同じコンテンツに移動できます。
また、ダイナミックリンクはアプリのインストールが必要な場面でも利用できます。たとえば、ダイナミックリンクを開いたユーザーの iOS デバイスまたは Android デバイスにアプリがインストールされていない場合にインストールを促し、インストール完了後にアプリを起動してリンクを開くといったこともできます。3-2. App Store IDとチームIDを取得する
ダイナミックリンクを作成する際に、「
App Store ID
」と「チームID
」が必要となるので先に取得しておきます。
すでに取得済みの方はスキップしてください。チームIDの取得
Apple Developerアカウントの[
Certifications, IDs & Profiles
]セクションの[Identifiers
]セクションで確認できます。「App ID Prefix
」がチームIDです。
アプリを未登録の方は、以下の手順で登録できます。
[Certifications, IDs & Profiles
]セクションをクリックします。
[Identifiers
]セクションの+
ボタンをクリックします。
「App IDs
」を選択して「Continue
」で次に進みます。
「Select a type
」で「App
」を選択して「Continue
」で次に進みます。
「Description
」にはアプリの簡単な説明を入力します。アプリ名でも良さそうです。
「Bundle ID
」にはXcodeプロジェクトのBundle Identifierを正確に入力してください。
「Capabolotoes
」では、アプリリリース時にはアプリの機能を漏れなく選択する必要がありますが、今回は最低限必要な「Associated Domains
」を選択して次に進みます。
「App ID Prefix
」がチームIDです。これでチームIDの確認を完了しました。
App Store IDの取得
App Store Connectの「
マイApp
」から自分の登録しているアプリをクリックして、[App情報
]セクションにある「Apple ID
」がApp Store IDです。
アプリを未登録の方は以下の手順で登録できます。
「マイApp
」をクリックします。
作成しているアプリを「新規App
」から追加します。
「アプリの名前
」、「使用する主な言語
」、「Bundle ID
」、「SKU
」を設定します。
SKUはアプリを区別できる名前であれば何でも良いと思います。
今回はアプリ名にしました。
作成を終えたら、[App情報
]セクションをクリックして「Apple ID
」を確認します。これがApp Store IDに当たるので、確認完了です。
3-3. Dynamic Linksを作成する
公式ドキュメントによると、Dynamic Linksの作成方法は4つありますが、今回はFirebase コンソールで作成します。
[Dynamic Links
]セクションを開き、「始める
」をクリックします。
Dynamic LinksのURLドメインを作成します。
ドメインは特に指定しなければGoogle提供の.page.link
が与えられるので、それを使用します。
サブドメインはアプリ名などの分かりやすいものが良いです。(例えば、今回ならURLをopenapp.page.link
にするとか)
次に、「新しいダイナミック リンク
」ボタンからダイナミックリンクを作成します。
短縮URLのリンク設定では、デフォルト値でも問題ありません。今回は分かりやすいように「open」としています。
次に、ダイナミックリンクの設定です。
ディープリンクURLによって特定のコンテンツに直接遷移することが可能となります。ディープリンクは、オリジナルのURLで良いようなので遷移先が分かるように設定します。
今回は、アプリ自身に移動するので、[Authentication
]セクションの承認済みドメインURL
をコピーして使用しています。
「ダイナミックリンク名
」は処理が分かりやすいように、「Open App」のようにします。
次に、iOSデバイスでリンクを開いたときの動作を設定します。
今回は「ディープリンクでiOSアプリを開く
」を選択して、アプリを指定します。
このとき、Firebase コンソールの[プロジェクト設定
]セクションにおいて、App Store ID
とチームID
を設定している必要があります。
設定していない場合、アプリを指定しようとすると、「App Store IDとチームIDをここに追加してください
」という警告が表示されるので、未設定の方は入力してください。
次に、アプリがインストールされていないときの動作を設定します。
今回はApp Storeに移動します。
Androidでの動作設定や、その他詳細オプションを設定できますが、今回は特に何も設定をせずに進めます。
以上で、ダイナミックリンクの作成は完了です。
リンクURLをコピーして、[Authentication
]セクションの[Sign-in method
]タブ内にある「承認済みドメイン
」に、ダイナミックリンクURLのドメインを追加します。
下記のように、追加したURLドメインが一覧に表示されていればOKです。
以上で、コンソールにおけるDyanmic Linksの構成は完了です。
次にXcodeプロジェクトの設定を行います。3-4. Xcodeプロジェクトの設定
「
TARGETS
」の[Signing & Capabilities
]タブにある「+Capability
」をクリックして「Associated Domains
」を追加します。
「Domains
」に「applinks:example.page.link
」を入力してください。「example.page.link
」はFirebaseコンソールで作成したダイナミックリンクURLのドメインです。
次に、同じく「TARGETS
」の[Info
]タブで、「URL Types
」を開いて+
をクリックし、「Identifier
」と「URL Schemes
」を入力します。
「Identifier
」にはアプリ名を入力しておきます。
「URL Schemes
」にはアプリのBundle Identifierを入力します。
以上で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. メールアドレスに認証リンクを送信する
ユーザーのメールアドレス
actionCodeSettings
を使用して、ユーザーのメールアドレスに認証リンクを送信します。
ログインメールの送信時に、メールアドレスをローカルに保存しておきます。
Firebase Auth`では、認証フロー完了時にユーザーのメールアドレスとログインリンクの送信アドレスが一致するかどうかを確認するため、ローカルに保存しておくことで、ユーザーに再度メールアドレスを入力してもらわなくても認証フローを完了できます。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. リンクを確認して、ログインを完了する
リンクを受信したら、それがメールリンク認証用であることを確認してログインを完了します。
ユーザーがメールに含まれたリンクをタップすると、次のようなページに遷移します。
「OPEN
」ボタンをタップするとアプリが開きます。 (アプリがインストールされていない場合は、AppStoreを開きます)
アプリを開くと、SceneDelegate
クラスのscene(_:continue:)
メソッドが呼ばれるので、この中にログイン完了の処理を書きます。今回は、サインインしたら画面を切り替えるというシンプルな処理にしています。
公式ドキュメントには、AppDelegate
クラスのapplication(_:continue:restorationHandler:)
メソッドの中で処理を書いていますが、iOS13以降ではダイナミックリンクからアプリを開いたときにapplication(_:continue:restorationHandler:)
メソッドに処理を書いても機能しないようです。
下記の記事が分かりやすく、参考にさせていただきました。
参考: [iOS13] SceneDelegateでDynamicLinksからのURLをハンドリングするSceneDelegateimport 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. [補足] サインアウトする
サインアウト処理は以下のように簡単に書けます。
DidSignViewControllerimport 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アップデートなどにより、そのまま手順通りにやっても上手くいかない箇所もあるため、合わせてこの記事がほんの少しでも役に立てば幸いです。
初めて記事を投稿したため、分かりづらい点が多いと思いますが、今後もアップデートしていければと思います。参考文献
この記事は以下の情報を参考にしました。