この記事は Flutter Advent Calendar 2019 16日目の記事です。
Flutter使ってみたいなーと思っていたので、まずはFirebase Auth + Google認証を組み込んだ上で、Flutter Webでの挙動を確認してみたよ。という内容になっています。主にWebでの挙動確認が主目的です。参考にしたサンプルの品質が若干怪しいので、それだけ気をつけてください。
Firebase Auth + Google認証の組み込み
Flutter Webは直近ベータになったようですが、まだ知見も少ないので、まずはモバイル(Android)でちゃんと動くかどうかを確認してから進めました。まずは下準備として、以下の記事あたりを参考に、Androidで認証フローを一通り確認できるようにしています。
- https://blog.codemagic.io/firebase-authentication-google-sign-in-using-flutter/
- https://medium.com/flutter-community/flutter-implementing-google-sign-in-71888bca24ed
- https://levelup.gitconnected.com/using-firebase-in-flutter-web-4b99952180aa
認証部分のコードは以下のようになっています(記事からの引用)
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 ですね。
<!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
Google OAuth と Firebaseの接続設定
Androidだと android/app/google-services.json をぶっこむだけでしたが、Web版の場合はindex.htmlのヘッダに接続情報を記述します。また、firebaseのスクリプトを読ませる必要があります。
<!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に記述します。
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について書いてくれます!楽しみ!