💬

FlutterでGoogle/Apple認証 on Firebase

に公開

FlutterでFirebase Authenticationを利用し、Google認証・Apple認証を実装しました。巷で見かける情報通りで行かない部分もあり、結構苦戦したので書き留めておきます。

ゴール

最終的にはこれができるようになりました。

  • Androidで
    • Google認証でサインインができる
  • iOSで
    • Apple認証でサインインができる
    • Google認証でサインインができる

ポイントとしては、『AndroidでApple認証をやらない』ですね。これはちょっと面倒くさそうだったということと、Android使ってるユーザーなんてGoogle認証ありゃいいでしょ、と高をくくった結果であります。(やりたいって方は、調べた内容を後述してあるので参考にしてみてください)

環境

時代とともに変わる部分もあるかと思いますので、各種バージョンを記しておきます。

  • Mac OS 15.5
  • Flutter 3.27.4
  • firebase_core 3.14.0
  • firebase_auth 5.6.0
  • google_sign_in 6.3.0

Firebaseプロジェクトの作成

いの一番にやることはFirebaseプロジェクトのセットアップです。おすすめは、開発初期から本番環境用プロジェクトも作っておくことです。この手の設定って、あとから振り返ると「あれ・・、どこどこいじったんだっけ・・・?」となりがちなので、最初にまとめてやっておいたほうが楽で確実です。どうせFirebaseは無料枠あるので作って寝かしておいてもお金かからないし。

私はFirebase コンソールから、ポチポチチと作りました。

(この作業は必須ではないですが)ちゃんと本番環境用のプロジェクトは『環境の種類』を本番環境にしておきましょう。赤いアイコンが付いて間違えることが少なくなると思います。

CLIのセットアップ

firebase CLI

最初にfirebase CLIのインストールが必要です。次のサイトに従ってインストールします。私はnpmで入れました。

https://firebase.google.com/docs/cli?hl=ja#mac-linux-npm

npm install -g firebase-tools

flutterfire

次にflutter+firebase用のCLIであるflutterfireをセットアップします。基本的には次のサイトに沿ってやっていきます。

https://firebase.google.com/docs/flutter/setup?hl=ja&platform=ios

firebaseにログインします。

firebase login

そしてアクティベートが必要です。

dart pub global activate flutterfire_cli
# fvm使ってる人は↓こちら↓
fvm dart pub global activate flutterfire_cli

これでflutterfireというコマンドが使用できるようになります。

Flutterプロジェクトに適用

Firebaseを導入したいFlutterプロジェクトのルートで次のコマンドを実行します。このとき、Google認証やApple認証に必要なパッケージもあわせてインストールします。

flutter pub add firebase_core firebase_auth google_sign_in
# fvm使ってる人は↓こちら↓
fvm flutter pub add firebase_core firebase_auth google_sign_in

configureコマンドで初期化します。flutterfireに色々と汚されるので、あらかじめコミットしておきましょう。コマンドを実行すると、「どのFirebaseプロジェクトを使用するんだ?」的なことを聞かれるので、開発環境用のプロジェクトを選択します。あと、適用するプラットフォームとか聞かれるので、iOSとAndroidを選択しておきます(ここはプロジェクトに応じてお好きなように)。

flutterfire configure

しばらくガチャガチャしてコマンドが正常に終了したらgitの差分を見てみましょう。きっと、lib直下にfirebase_options.dartというファイルができているはずです。こいつの中身を見てみると、apiKeyappIdとかが設定されているはず。これはFirebaseプロジェクトの設定画面に記載されている内容で、flutterfireが自動的に設定してくれたのですね。ちなみにFirebaseプロジェクトの設定画面とは、↓この『プロジェクトの設定』メニュー↓からいけるところです。

ではこの設定値を使用してFirebaseを初期化するようにlib/main.dartに必要なコードを挿入します。

lib/main.dart
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
...
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  ...
  // このコードを挿入
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  ...
  runApp(const MyApp());
  ...

開発環境用はこれでOKなのですが、本番環境用の設定が必要です。ビルドによって使用する設定値を切り替えられるように次のような感じでfirebase_options.dartを編集します。各値をどう設定するかは後に登場するgoogle-services.jsonGoogleService-Info.plistを見れば分かると思います。

lib/firebase_options.dart
  ...
  static const FirebaseOptions android = kReleaseMode
      ? FirebaseOptions(
          apiKey: '本番用のapiKey',
          appId: '本番用のappId',
          messagingSenderId: '本番用のmessagingSenderId',
          projectId: '本番用のprojectId',
          storageBucket: '本番用のstorageBucket',
        )
      : FirebaseOptions(
          apiKey: '開発用のapiKey',
          appId: '開発用のappId',
          messagingSenderId: '開発用のmessagingSenderId',
          projectId: '開発用のprojectId',
          storageBucket: '開発用のstorageBucket',
        );
  ...

上記はandroidの例ですが、iOSも(必要ならWebも)同様にやっておきます。

認証の基本準備に関してはこれで完了です。といってもまだまだ設定は必要なのですが、順に説明していきます。

Google認証

基本的にはここに情報が詰まっています。

https://firebase.google.com/docs/auth/flutter/start?hl=ja

実装

Google認証の実装についてはこのページですね。メインとなるサインイン処理はこれぐらいです。

import 'package:google_sign_in/google_sign_in.dart';

Future<UserCredential> signInWithGoogle() async {
  // Trigger the authentication flow
  final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();

  // Obtain the auth details from the request
  final GoogleSignInAuthentication? googleAuth = await googleUser?.authentication;

  // Create a new credential
  final credential = GoogleAuthProvider.credential(
    accessToken: googleAuth?.accessToken,
    idToken: googleAuth?.idToken,
  );

  // Once signed in, return the UserCredential
  return await FirebaseAuth.instance.signInWithCredential(credential);
}

ただ個人的にはいくつか補足が必要かなと思いました。

SignInメソッドはアカウント作成・ログイン兼用

FirebaseのsignInWithCredentialメソッドは、該当するアカウントが存在しない場合は自動的に新規アカウントを作成し、存在する場合はそのアカウントでログインします。つまり、アカウント作成とログインを別々に実装する必要がありません。ただ、アカウント作成なのかログインなのかで認証後のメッセージは分けたかったので、次のコードで見分けることにしました。

final result = await FirebaseAuth.instance.signInWithCredential(credential);
result?.additionalUserInfo?.isNewUser ?? false //アカウント作成だったらtrueになる
「Googleでサインイン」ボタンはガイドラインがある

以下のリンク先にそれがあります。一応則ってないといけないらしいので、ちゃんとデザインしてあげないといけません。

https://developers.google.com/identity/branding-guidelines?hl=ja

なお、その手の認証ボタンを集めたpubパッケージもあるようなので、楽したければそっち使ったほうがいいかもしれません。(私は依存関係を減らしたいので自作しました)

Google認証だとdisplayName, email, photoUrlが取れる

GoogleでサインインするとuserのdisplayNameemailphotoUrlが取得できます。photoUrlがあるとプロフィール画面のアバター画像とか簡単に出せるので便利。メールアドレスを欲するアプリ開発の場合も便利なんじゃないかと思いました。このあたり、取れるもの取れないものは認証プロバイダによります。

ログインプロバイダの追加

さてボタンを置き、先のコードを呼び出すようにしてもまだGoogle認証は動きません。まず、ログインプロバイダにGoogleを追加しないといけません。Firebaseプロジェクトの[Authentication]-[ログイン方法]からGoogleを有効にするだけです。

さらに設定は続く・・・ここからはAndroid/iOSで別立てになってます。

for Android

SHA1証明書(開発用)

前述のログインプロバイダ追加のダイアログにも書かれていたのですが、SHA証明書のフィンガープリントをFirebaseプロジェクトにアップロードする必要があります。開発用のフィンガープリントは次のコマンドで表示されるSHA1のフィンガープリントをコピーします。(本番は後述)

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

コピーしたら、貼り付けに行きます。例のプロジェクトの設定画面でフィンガープリントを登録します。

SHA1証明書(本番用)

では本番はどうするかというと、私の場合はすでに公開済みのアプリだったのですでに署名済みでした。Google Play Consoleの次の場所からゲットできます。

開発同様、本番用のFirebaseプロジェクトにSHA1のフィンガープリントを登録しておきます。

gradleの設定

(ひょっとしたらflutterfireが勝手にやってくれたかもしれないが一応記載)
ちょこちょこ、ビルド設定に追加が必要みたい。

android/app/build.gradle
plugins {
    id "com.android.application"
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
+   id "com.google.gms.google-services"
}

...
defaultConfig {
-   minSdkVersion flutter.minSdkVersion
+   minSdkVersion 23
android/settings.gradle
plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "8.4.1" apply false
    id "org.jetbrains.kotlin.android" version "2.0.0" apply false
+   id "com.google.gms.google-services" version "4.4.0" apply false
}

google-services.json

最後にキーjsonをFlutterプロジェクトに仕込む必要があります。Firebaseプロジェクトの設定画面からgoogle-service.jsonをダウンロードします。

ダウンロードしたらFlutterプロジェクトの次のディレクトリに配置します。(ディレクトリがなければ作成してください)

  • android/app/src/debug/google-services.json ... 開発用
  • android/app/src/release/google-services.json ... 本番用

これでビルドに応じたファイルが使用されるようになっています。あと忘れずに.gitignoreにも追加しておきましょう。

google-services.json

AndroidはこれでGoogle認証ができるようになっているはず。エミュレーターでもちゃんと動くので試してみましょう。

for iOS

iOSのほうは挫けそうになるぐらい面倒くさいです。

GoogleService-Info.plist

まずAndroidでいうgoogle-services.jsonにあたるGoogleService-Info.plistというファイルを仕込む必要があるのですが、これが開発/本番を切り替えられるようにするのにひと手間必要です。

まずGoogleService-Info.plistをダウンロードします。これはgoogle-services.jsonと同様にFirebaseプロジェクトの設定画面からダウンロードできます。

これを開発用と本番用をそれぞれダウンロードしてきて、それぞれ GoogleService-Info_Debug.plist GoogleService-Info_Release.plist にリネームします。2つのファイルを選択してドラッグしたら、XcodeでRunnerの中にドロップします。できたらこんな感じになるはず。

続いて、ビルド時に自動的に適した方を使用するようにスクリプトを仕込みます。以下のサイトが大変参考になりました。

https://toconakis.tech/flutter-firebase-test-prod/

私はならってこのように設定しました。

cp -f "$SRCROOT/GoogleService-Info_${CONFIGURATION}.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"

これでビルド環境に応じたファイルが使用されることとなります。

URL Types

これがないとiOSでGoogleでサインインしようとするとクラッシュします。Xcodeからも設定できますが、Info.plistを直接編集したほうが早いと思います。以下の通り追加。設定する値はGoogleService-Info.plistのREVERSED_CLIENT_IDになります。arrayなので開発用・本番用まとめて設定できます。

ios/Runner/Info.plist
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<!-- 開発用・本番用ともに -->
				<string>com.googleusercontent.apps.XXXXXX</string>
				<string>com.googleusercontent.apps.XXXXXX</string>
			</array>
		</dict>
		<dict/>
	</array>

以上でiOSでもGoogle認証が利用できるようになります。
あーしんどかった。。。

Apple認証

では続いてApple認証の方です。iOSだけを対象にしています。

実装

サインインの処理は基本的にはこれだけです。

  Future<UserCredential?> signInWithApple() async {
    final appleProvider = AppleAuthProvider();
    return await _auth.signInWithProvider(appleProvider);
  }

googleの方は専用のpubパッケージを入れましたが、Appleの方は何も要りません。簡単ですね。

displayName, email, photoUrlは最初しか取れない

(自分で試したわけじゃないんですが)
Google認証の場合と異なり、displayNameemailphotoUrlはサインアップした初回しか取得できないらしいです。これはAppleのポリシーによるもので、個人を特定しかねないデータの取り扱いが厳しいらしいです。というわけで、上記項目がどうしても欲しい場合はサインアップ時にアプリ内のユーザー情報データなどに書き込んでおかないといけないみたい。

App IDにSign in with Apple

Apple Developer ProgramのCertificates, Identifiers & Profilesに行ってAppleサインインの権限を付与します。私はリリース済みのアプリだったので、すでにあるApp IDを下の画面から選択しました。

一覧の中からSign In with Appleを探し出しチェックを入れます。その後、[Edit]ボタンを押し、出てくるダイアログは特に何も入れないままSaveする。で保存したら完了。

Runner.entitlements

XcodeのSigning & CapabilitiesからSign In with Appleを追加します。

コードで編集する場合は以下です。

ios/Runner/Runner.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>aps-environment</key>
	<string>development</string>
+	<key>com.apple.developer.applesignin</key>
+	<array>
+		<string>Default</string>
+	</array>
</dict>
</plist>

以上です。
これでApple認証の方も無事サインインできるようになるはず!

AndroidでもApple認証したい人向け

ログインプロバイダの追加のダイアログを埋めればいいようです。

やり方はこのページからのリンクを辿っていけば見つかりますが、結構ややこしいです。

https://firebase.google.com/docs/auth/flutter/federated-auth?hl=ja#apple

エッセンスだけ書くと、

  • Apple Developer Programへいって、Service IDを追加
  • そのService IDにSign in with Appleを付与
    • その際、さきほどのダイアログ下部に記載されているコールバックURLを指定する
  • さらにKeysからKeyを作成
    • そのKeyの名前と秘密鍵(ダウンロードできる)をさきほどのダイアログに貼り付ける
  • AppleチームIDを入れる(Apple Developer Programに載ってる)

ということです。

おわりに

なんかめちゃくちゃ大作になってしまった・・・。記事を分けてBookにしたほうが良かったかも🤔

「間違ってる」「あれはどうなんだ?」的なことがあったらコメントお気軽にどうぞ。

Discussion