Open4

Firebase Authentication+FirebaseUIを使った認証システムの構築メモ【WIP】

yamacraftyamacraft

このスクラップについて

自分用に新規でユーザー認証を必要とするサービスを作る場合の参考として、いろいろと書いてまとめていくスクラップになります。

自分の中で必要な分がまとまってきたら、ブログの方にまとめを記載し、こちらのスクラップはcloseします。

また、いったんはAndroid限定で記載します。

前提条件

  • FirebaseUIを使う
  • 認証はGoogle認証、メールリンク認証、匿名(Anonymous)認証のみ実装する
  • 認証後の各ユーザーデータの管理方法についても考える
  • 基本的にFirebase以外のmBaaSやSaaSは使わず、Firebaseのみで完結する

各前提条件についての補足

Firebase UIを使うことについて

最大の理由は工数(手間)の削減。各ログインの導線のUIだけでなく、メールリンク認証に関してはメールの再送信画面も用意してくれるのが嬉しい。

UI自体は以前は英語のみだった記憶があるが、現時点では日本語も対応してくれるのもありがたい。

Google認証、メールリンク認証、匿名認証のみの実装について

TwitterやFacebook認証などは、認証のためにアプリケーション登録を行うこと自体に手間がかかるため。
特にFirebaseUIを経由して認証を実施するので、突然の認証方式の変更があった場合にこちらのタイミングで対応できないケースが多発しそう。

メールリンク認証なのは、なるべくパスワードを保管するようなことを避けたいため。
できればメールアドレスも保管したくはないのだけれど、まあデータ自体はFirebase内で管理してくれるし、これだけで(そのメールアカウント自体がハックされていなければ)第三者が不正アクセスできるわけでもないのでいいかなと。

匿名認証は、お試しで最低限の機能を提供する窓口があったほうがいいかなという理由から。

各種参考リンク

yamacraftyamacraft

Firebaseの管理画面でやる下準備

Firebase Authenticationで必要な認証をONにする

Firebaseの管理画面にアクセスし、対象のプロジェクトを選んで「Authentication」を開き、「Sign-in method」をクリック。

  • メール / パスワード を開いて、2つの「有効にする」をONでメールリンク認証が機能する
  • Google を開いて「有効にする」をONでGoogle認証が機能する
  • 匿名 を開いて「有効にする」をONで匿名認証が機能する

認証するアプリの登録と証明書の追加

Firebaseの管理画面にある設定(歯車アイコン)をクリックして「プロジェクトを設定」をクリック。
ページ下部にあるマイアプリの項目の「アプリを追加」ボタンから、認証するアプリを追加する。

SHA証明書フィンガープリントは、keytoolで確認可能。

$keytool -list -v -alias alias名 -keystore keystoreのパス

追加したらgoogle-service.jsonをダウンロードしてAndroidプロジェクトに組み込む。ここは通常のFirebaseのプロジェクト追加と同じなので省略。

ダイナミックリンクでメールリンク認証用のURLを生成

メールリンク認証を行うためには、アプリへリダイレクトするためのURLが必要になる。
これはダイナミックリンクを使えばFirebase内で完結できる。

Firebaseの管理画面からDynamic Linksをクリックして、Dynamic Linksの作成に入る。
Dynamic Linksを一度も利用していない場合はURL接頭辞の追加が始まるので、それは公式ドキュメントを参考に適当に追加する。

Dynamic Linksの管理画面が出てきたら、「新しいダイナミックリンク」ボタンをクリックして、認証用のURLを作成する。

  • 短縮URLは適当でいい(signin という文字列では作成できないので注意)
  • ダイナミックリンクの設定は次の通りがよい
    • ディープリンクURLは、アプリがインストールしていない端末などで開かれた場合のリダイレクト先になるのである程度はちゃんと設定する
      • hosting( https://***.firebaseapp.com/ )を宛先に用意しておくのが無難
    • ダイナミックリンク名は管理画面の一覧で表示する名前なので適当なものをつける
  • Android用のリンク動作の定義では、「Androidアプリ内でディープリンクを開く」にして認証に使うアプリを選択する
  • これ以外はいったん適当でいい

Authenticationのホワイトリストに短縮URLのホストを追加

Firebaseの管理画面の「Authentication > Sign-in method」の下に承認済みドメインがあるので、ドメインを追加ボタンをクリックして短縮URLのホスト( ***.page.link )を追加する

yamacraftyamacraft

コード実装

appとなるmoduleのbuild.gradleに必要なライブラリを追加。

app/build.gradle
dependencies {
    // ....

    implementation platform('com.google.firebase:firebase-bom:26.5.0')
    implementation 'com.google.firebase:firebase-auth-ktx'
    implementation 'com.google.firebase:firebase-dynamic-links-ktx'

    implementation 'com.firebaseui:firebase-ui-auth:7.1.1'

    implementation 'com.google.android.gms:play-services-auth:19.0.0'
    // ....
}

とりあえずGoogle認証とメールリンク認証と匿名認証を全て含めたFirebaseUIによる認証ページの作成方法

MainActivity.kt
val actionCodeSettings = ActionCodeSettings.newBuilder()
    .setAndroidPackageName(packageName, true, null)
    .setHandleCodeInApp(true)
    .setUrl("サインイン用の短縮URL")
    .build()

val emailProvider = AuthUI.IdpConfig.EmailBuilder()
    .enableEmailLinkSignIn()
    .setActionCodeSettings(actionCodeSettings)
    .build()

val providers = listOf(
    AuthUI.IdpConfig.GoogleBuilder().build(),
    emailProvider,
    AuthUI.IdpConfig.AnonymousBuilder().build()
)

val authUI = AuthUI.getInstance()
    .createSignInIntentBuilder()
    .setAvailableProviders(providers)
    // ロゴの表示
    .setLogo(R.drawable.ic_baseline_account_circle)
    // 利用規約、並びにプライバシーポリシーのリンク追加
    .setTosAndPrivacyPolicyUrls(
        "https://example.com/terms.html",
        "https://example.com/privacy.html"
    )
    .build()

ここで作成した authUI はintentなので、これをstartActivityすればFirebaseUIの認証画面へ遷移する。(メモ:ActivityResult周りは後で書く)

ダイナミックリンクからの呼び出しで認証をかける

Google認証と匿名認証は上記だけで実装が完了するが、メールリンク認証は届いたメールのリンクをクリックしてアプリが開いてからの認証処理を作る必要がある。

まず、開きたいActivityにインテントフィルターを追加する

AndroidManifest.xml
<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <!-- hostに短縮アドレスのホストを追加 -->
    <data
        android:host="***.page.link"
        android:scheme="https"/>
</intent-filter>

対象のActivityにダイナミックリンクの処理を追加

MainActivity.kt
Firebase.dynamicLinks
    .getDynamicLink(intent)
    .addOnSuccessListener { dynamicLink ->
        dynamicLink?.link?.let {
            val actionCodeSettings = ActionCodeSettings.newBuilder()
                .setAndroidPackageName(packageName, true, null)
                .setHandleCodeInApp(true)
                .setUrl("サインイン用の短縮URL")
                .build()
            val emailProvider = AuthUI.IdpConfig.EmailBuilder()
                .enableEmailLinkSignIn()
                .setActionCodeSettings(actionCodeSettings)
                .build()
            val providers = listOf(emailProvider)

            val intent = AuthUI.getInstance()
                .createSignInIntentBuilder()
                .setEmailLink(it.toString())
                .setAvailableProviders(providers)
                .build()
            signInWithEmailLinkResult.launch(intent)
        }
    }
    .addOnFailureListener {
        Timber.e(it, "error: ${it.message}")
    }

actionCodeSettingsとemailProvider周りは最初に書いた認証とまったく同じ内容になるので、この辺りは何かしらで共通化した方が良いと思われる。

サインアウトについて

FirebaseAuth.getInstance().signOut() だけではGoogle認証の場合、次の認証時に前回認証に使ったGoogleアカウントを即使おうとするため、AuthUI経由でサインアウトする必要がある。

AuthUI.getInstance().signOut(this).addOnCompleteListener {
    //  サインアウト後に何か処理をさせたい場合ここに書く
}
yamacraftyamacraft

次やったり調べたりすること

  • 認証の連携(匿名からメールやGoogle認証への移行)
    • これは単にFirebaseUIを呼び出せば良さそう
  • ユーザー情報のFirestoreの管理方法(プロフ画像のstorage保存も含む)