Flutter Web x Firebase Auth x Google認証を実装してみる


この記事は Flutter Advent Calendar 2019 16日目の記事です。

Flutter使ってみたいなーと思っていたので、まずはFirebase Auth + Google認証を組み込んだ上で、Flutter Webでの挙動を確認してみたよ。という内容になっています。主にWebでの挙動確認が主目的です。参考にしたサンプルの品質が若干怪しいので、それだけ気をつけてください。

Firebase Auth + Google認証の組み込み

Flutter Webは直近ベータになったようですが、まだ知見も少ないので、まずはモバイル(Android)でちゃんと動くかどうかを確認してから進めました。まずは下準備として、以下の記事あたりを参考に、Androidで認証フローを一通り確認できるようにしています。

認証部分のコードは以下のようになっています(記事からの引用)

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();

Future<String> signInWithGoogle() async {
  final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
  final GoogleSignInAuthentication googleSignInAuthentication =
  await googleSignInAccount.authentication;

  final AuthCredential credential = GoogleAuthProvider.getCredential(
    accessToken: googleSignInAuthentication.accessToken,
    idToken: googleSignInAuthentication.idToken,
  );

  final AuthResult authResult = await FirebaseAuth.instance.signInWithCredential(credential);
  final FirebaseUser user = authResult.user;

  assert(!user.isAnonymous);
  assert(await user.getIdToken() != null);

  final FirebaseUser currentUser = await _auth.currentUser();
  assert(user.uid == currentUser.uid);

  return 'signInWithGoogle succeeded: $user';
}

void signOutGoogle() async{
  await googleSignIn.signOut();

  print("User Sign Out");
}

GoogleからIDトークン取得 → FirebaseへIDトークンを投げて認証 が大きな流れになっております。このサンプルではGoogleからトークンを取得するフローがImplicitっぽいのが気になりました。あとFirebase Authってリプレイアタックし放題なんですっけ。

...チョット本筋とズレてしまうので今回はこのまま進めます。

Flutter Web をビルドする準備

Webをビルドできるようにします。
ベータ版を有効にする必要があります。

flutter channel beta
flutter upgrade
flutter config --enable-web

flutter devicesを叩いて Chrome が確認できればOK

$ flutter devices

Android SDK built for x86 • emulator-5554 • android-x86    • Android 9 (API 28) (emulator)
Chrome                    • chrome        • web-javascript • Google Chrome 78.0.3904.108
Web Server                • web-server    • web-javascript • Flutter Tools

既存のプロジェクトにWeb版を追加する場合は以下コマンドでOKです。

flutter create .

実行すると、各種設定ファイルが生成されると思います。分かりやすいところだろ web/index.html ですね。

web/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>flutter_auth_playground</title>
</head>
<body>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

packages getしてパッケージを最新にしておきましょう。
次のコマンドWebを起動します。

flutter run -d chrome

動きました。やったね。

起動時のポートの固定方法

前述した起動方法だと、毎回ポートが変わります。
ポートが毎回変わるとOAuth 2.0のオリジン検証で弾かれるのが面倒なので、起動時のポートを固定しておくと捗ります。

flutter run -d chrome --web-port=3333

Android Studioの場合は下記から設定できます。

Google OAuth と Firebaseの接続設定

Androidだと android/app/google-services.json をぶっこむだけでしたが、Web版の場合はindex.htmlのヘッダに接続情報を記述します。また、firebaseのスクリプトを読ませる必要があります。

web/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="google-signin-client_id" content="0123456789-xxxxxxxxxxxxxxxx.apps.googleusercontent.com" />
  <title>flutter_auth_playground</title>
</head>
<body>
  <script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-auth.js"></script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

google-signin-client_id はGCPのOAuthクライアント作成で取得したクライアントIDを使います。
作成時に承認済みのURLに、先程ポートを固定したURIを設定しておきます。

次に、Firebase側でWeb用アプリを追加し、その際に発行されたfirebaseConfigを覚えておきます。

firebaseConfigを参考に、以下のような形でmain.dartに記述します。

main.dart
import 'package:firebase/firebase.dart' as firebase;

void main() {
  firebase.initializeApp(
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxxxx.firebaseapp.com",
    databaseURL: "https://xxxxxxxxxxx.firebaseio.com",
    projectId: "xxxxxxxxxxxx",
    storageBucket: "xxxxxxxxxxx.appspot.com"
  );

  runApp(MyApp());
}

設定はこれで終わりです。

動かしてみる

(Sign in with Google) をクリックしてGoogle認証を通すと...

動きました

気になっていたトークンの格納場所ですが、IndexedDBの中を掘っていったところのstsTokenManagerにアクセストークンもリフレッシュトークンも保存されていました。まあWeb Storageに入れるしかないよなーという感じですね。

まとめ

Flutter Web に Firebase AuthとGoogle認証を組み込んでみました。ほとんどコードを書き換えることなく各プラットフォームのビルドができるのは素晴らしい体験ですね。DOMではなくcanvasに描画されるので動きに若干の違和感はあるなーという感想でした。

明日はmirockさんがDesktop Embedding for Flutterについて書いてくれます!楽しみ!