2021年10月26日火曜日

Firebaseのメールリンクでログインしてみる

 AndroidアプリでFirebaseを使ったメールリンク認証を作る際、
躓いたのでまとめておきます。


メールリンク認証とは

メールアドレスのみで認証する、パスワード設定が不要な認証方法です。

手順は
  1. アプリのユーザ登録画面からメールアドレスを入力してもらう
  2. Firebaseからそのメールアドレスに招待メールが送られる
  3. メールの招待RULをクリックすると、アプリが起動し認証完了(ユーザ登録完了)

メールを受け取れるということは本人である証明。というセキュリティの考え方。


Firebase側の実装

プロジェクトを作る

Firebaseのコンソールを開いて、「プロジェクトを追加」を押下
プロジェクト名は「MailLoginSample」※なんでもいい
Google アナリティクスはどちらでもいいと思うけど有効にした
Google アナリティクスの構成もデフォルトを選択

アプリを追加する

AndroidアプリとFirebaseを接続する為に「アプリを追加」する
作成したプロジェクトに入ると、「アプリを追加する」があるので追加していく
プラットフォーム選択は「Android」にする

入力項目のパッケージ名は、
AndroidSdudioで新しくプロジェクトと作るときに設定するパッケージ名

すでに作ってるアプリに機能実装したいけど、
パッケージ名何にしたかわからないときはGradleのapplicationIdを見る

アプリのニックネーム、証明書は未入力でよい

次に、設定ファイルのダウンロードする


ダウンロードした 「google-services.json」は
AndroidStudioの画面で「app」にドラッグ&ドロップで置いておく


メールでの認証を有効にする

構築 -> Authentication の「Sign-in method」タブを開く
ログイン プロバイダの項目で、「新しいプロバインダを追加」を押す
ポップアップが出てくるので、「メール」を選択
以下の二つを有効にする
・メール/パスワード
・メールリンク(パスワードなしでログイン)


Dynamic Linksでドメインを作る

AndroidSdudioで実装時に、アプリからFirebaseに認証メール送信要求を出す。
その時のパラメータにURL指定項目があり、そこで使うドメインを作成する

左のバーからDynamic Linksを押下(右図参照)

新しいドメインを作る。
末尾に「.page.link」を入れると無料のカスタム サブドメインとして生成できる。今回は
ドメイン名は「mailloginsample.page.link」としておく


参考

https://firebase.google.com/docs/dynamic-links?hl=ja


その後、URLを認証する必要があるので

構築 -> Authentication の「Sign-in method」タブを開く
承認済みドメインの項目で、「ドメインを追加」を押し、先ほど作ったドメイン(mailloginsample.page.link)を入力する


Firebaseの準備はこれで完了!!
ドメインを作る情報が見つけられず少し手間取った...


AndroidStudio側の実装

SDK等開発環境構築

「google-services.json」の取り込みは、上で説明したので割愛
承認に必要なSDKを取り込んでいく
「project/build.gradle」に以下の★の1行を追加
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.3"
        classpath 'com.google.gms:google-services:4.3.10'  //★コレ
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
「app/build.gradle」の以下の★の行を追加(バージョンは最新のものが望ましい)
plugins {
   :
   :
    id 'com.google.gms.google-services' // ★追加
}

 :
 :

dependencies {
    implementation platform('com.google.firebase:firebase-bom:28.4.2')// ★追加

    implementation 'com.google.firebase:firebase-analytics'           // ★追加
    implementation 'com.google.firebase:firebase-auth'                // ★追加
    implementation 'com.google.firebase:firebase-firestore'           // ★追加
    implementation 'com.google.android.gms:play-services-auth:19.2.0' // ★追加
    :
    :
}

画面設計

今回はテストなので1画面内に以下のViewを配置する
  • 入力エリア
  • ⇒招待メールを送ってもらうメールアドレスを入力させる
  • 「招待メール」ボタン
  • ⇒ 招待メールをFirebaseに要求する
  • ログイン認証ボタン
  • ⇒ログイン認証を行う




実装

①メールアドレスを入力してもらい、招待メールをもらう
招待メール送信要求はString型のメールアドレスさえあれば作れるのでメソッド化しておく
招待メールボタン押下を契機(onClickSendMail)に、ExitTextから文字列を取得しFireabseへ要求する。
    /** 招待メールボタン押下イベント
     * Firebaseに招待メールを送信するように要求する */
   public void onClickSendMail(View v) {
       TextView tv = findViewById(R.id.editText);
       requestEmail(tv.getText().toString());
    }

    /** firebaseに招待メールを要求 */
    private void requestEmail(String email) {
       ActionCodeSettings actionCodeSettings = ActionCodeSettings.newBuilder()
               .setUrl("https://mailloginsample.page.link") // ★ Fireabase側の実装で、dynamicLinkで作ったドメイン
               .setHandleCodeInApp(true)                    // ★ 必ずTrueでないとエラー
               .build();
       FirebaseAuth auth = FirebaseAuth.getInstance();
       auth.sendSignInLinkToEmail(email, actionCodeSettings)
               .addOnCompleteListener(task -> {
                   if (task.isSuccessful()) {
                       Toast.makeText(this, "このメールアドレスにメールを送信しました。", Toast.LENGTH_SHORT)
                               .show();
                       // メールアドレスは不揮発領域に保存しておく
                       getSharedPreferences("mail", MODE_PRIVATE).edit().putString("address", email).apply();
                   } else {
                       Toast.makeText(this, String.valueOf(task.getException()), Toast.LENGTH_LONG)
                               .show();
                   }
               });
    }
ミソは21行目。
メール要求に成功したとき、入力したメールアドレスを不揮発に保存しておく。
メールを受けてリンク押下でこのアプリが立ち上がった時、新規Activity起動となる為、メアド覧が空になり再入力してもらう羽目になる。なので、ここで保存して、起動時に表示させる仕組みにする。

②メールを受信しリンク押下で本アプリを起動させる
AndroidManifest.xmlを編集することで、スマホで特定のドメインのURLへ遷移する時、本アプリが立ち上がる設定を入れておく。
<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />

        <!-- ★ここから追加 -->
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data
        android:host="mailloginsample.page.link"
        android:scheme="https"/>
        <!-- ★ここまで -->

    </intent-filter>
</activity>
13行目が大事で、ここは先ほども設定したdynamicLinkで作ったドメイン。
これで「https://mailloginsample.page.link」から始まるRULをクリックしたら、本アプリが立ち上がるようになる。

公式での解説だとこの辺り

 ③ログイン認証を行う
メールを受けて、メールのリンクを押下して本アプリが起動したときに限り
Intentにメールリンク情報(URI)が入っている。
メールアドレスと、このメールリンク情報の二つがあれば認証が行える。
①同様に認証をメソッド化し、認証ボタン押下契機(onClickSignIn)と合わせて以下となる
    /** ログイン認証ボタン押下イベント
     * Firebaseにログイン認証を要求する */
    public void onClickSignIn(View v) {
       // メールのリンクから起動したときはIntentにemailLink情報が入っている
        Intent intent = getIntent();
        String emailLink = intent.getData().toString();

        // 入力エリアからメールアドレスを取得
        TextView tv = findViewById(R.id.editText);
        String email = tv.getText().toString();

        // 認証要求
        requestSignIn(email, emailLink);
    }

    /** fairebaseに認証要求 */
    private void requestSignIn(String email, String emailLink) {
        FirebaseAuth auth = FirebaseAuth.getInstance();
        if (auth.isSignInWithEmailLink(emailLink)) {
            auth.signInWithEmailLink(email, emailLink)
                    .addOnCompleteListener(task -> {
                        if (task.isSuccessful()) {
                            AuthResult result = task.getResult();
                            Toast.makeText(this, "認証成功:IDは" + result.getUser().getUid(), Toast.LENGTH_LONG).show();
                        } else {
                            Toast.makeText(this, String.valueOf(task.getException()), Toast.LENGTH_LONG).show();
                        }
                    });
        } else {
            Toast.makeText(this, "リンクが正しくありません。emailLink=" + emailLink, Toast.LENGTH_LONG).show();
        }
    }
このLink情報もメアド同様に不揮発に保持しておけば、
次回以降は自動で認証完了することもできる。

以上で、メールリンク認証をAndroidで実装する方法は完成!!

終わりに

上で触れていない、不揮発に書いたメアドを復活させるコードなども含めて
全体のjavaコード。Kotlinコードを以下に載せておく。

0 件のコメント:

コメントを投稿