Flutter: MacデスクトップアプリからのGoogle SignInが突如エラーになる
結論:「google_sign_in_dartio」パッケージを利用する、もしくはセキュリティソフトを無効化する
2022/7/17 Flutter エラー・バグ日記
以前、こちらの日記にも記載したが、Flutter2.10にアップグレードしたタイミングから、「エラー400: invalid_request」が出て、突然、MacからのGoogleサインインができなくなった(サインインの目的はGoogle Driveへのアクセス)。
しばらく対応を塩漬けしていたが、、、今回調査していくつかの解決方法が分かったのでメモ(後述のとおり、Flutterのアップグレード自体は関係なかった。。)。
Flutterで一般的に使用されるGoogleサインイン用のパッケージ「google_sign_in」は、iOS、Android、Webのみの対応のため、デスクトップアプリにGoogleサインインを実装するためは、他の方法を模索する必要がある。
当初、対応方法を調べて「googleapis_auth」パッケージを用いれば、ブラウザを経由してGoogleサインインできると分かったので、下記方法(要点のみ掲載)で実装していた。
// クラス名、メソッド名、プロパティ名(変数名)について、 // パッケージ等で規定済の名称と区別するため、筆者が作成したもの(名前変更可のもの) // の名前の末尾には、大文字のオー「O」を付記 import "package:googleapis_auth/auth_io.dart"; import 'package:url_launcher/url_launcher.dart'; import 'package:googleapis/drive/v3.dart' // ・・・(略)・・・ // ここではGoogle Driveを使用する例として、scopeを設定 scopeO = [DriveApi.driveAppdataScope,]; // Googleサインイン・ユーザー同意の画面をブラウザで表示するためのメソッドを設定 void promptO(String urlO) async { if (await canLaunch(urlO)) { await launch(urlO); } else { throw 'Could not launch $urlO'; } } // "・・・・"には、Google Cloudで取得したClientIDを入れる var clientIdO = ClientId("・・・・", ""); // ここでブラウザを起動させてサインイン画面を表示 httpClientO = await clientViaUserConsent(clientIdO, scopeO, promptO); // Google Drive APIのインスタンスを作成 // 以降、このインスタンスを用いてGoogle Driveへのアクセス処理を記述する googleDriveApiO = DriveApi(httpClientO); // ・・・(略)・・・
(以下、参考情報)
↓Dart向けのGoogle API公式説明
↓Google カレンダーへのアクセス実装例
しかし、この方法が使えなくなってしまい、調べても解決策を見いだせなかったので、仕方なく他の方法を探すことにした。
他のMacデスクトップからのGoogleサインイン方法
結論としては、以下2つのパッケージを使う方法が見つかった。
① 「desktop_webview_auth」 を使う方法
以下のイシューで、本パッケージの具体的な実装例が紹介されている。
② 「google_sign_in_dartio」 を使う方法
こちらは、「pub.dev」内で関連しそうなパッケージが無いか探したところ、見つかった。
①「desktop_webview_auth」については、パッケージのExampleや、上記イシューの実装例を元に試したところ、確かにGoogleサインインの画面を起動でき、ユーザー同意を取得の上、サインインさせることができた。
しかし、恥ずかしながら、自分の理解が及ばず、「サインアウト」させる方法が分からなかった。。
※ソースコード内をひたすら探したが、サインアウトさせるメソッドが見つからなかった(無い訳はないと思うのだが、、、)。
そこで、②「google_sign_in_dartio」の方も試してみた。
ネット上に実装例が見当たらないので、パッケージのExampleを元に実装したところ、こちらもGoogleサインインの画面を起動でき、ユーザー同意を取得の上、サインインさせることができた。
しかも、実装方法が非常に簡単で、通常のiOS、Android用に「google_signin」パッケージを実装するコードの前に、以下のコードを入れるだけだった。
await GoogleSignInDart.register(clientId: "・・・・");
※"・・・・"の部分には、パッケージの説明ページのとおり、Google Cloudで、デスクトップ用に取得したClient ID(「アプリケーションの種類」で「デスクトップアプリ」を選択して作成)を記載する。
若干の注意点としては、「scopes:」の設定時に、「'email'」と「'profile'」も入れておく必要がある点。
当初、これらを入れずにscopesを設定したところ、サインイン画面は起動するものの、「Null check operator used on a null value」のエラーが出て、サインインできなかった。
具体的な実装例(一部のみ)は以下のとおり。
import 'package:google_sign_in_dartio/google_sign_in_dartio.dart'; import 'package:google_sign_in/google_sign_in.dart' import 'package:googleapis/drive/v3.dart' import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sign_in_as_googleapis_auth.dart'; // ・・・(略)・・・ // ↓この1文を追加するのみ await GoogleSignInDart.register(clientId: "・・・・"); // 以降はiOS、Android用に記述するコードと同じ googleSignInO = GoogleSignIn( scopes: [ // この2つがないと、nullエラーになるため必要 'email', 'profile', DriveApi.driveAppdataScope, ]); await googleSignInO.signIn(); httpClientO = (await googleSignInO.authenticatedClient())!; googleDriveApiO = driveO.DriveApi(httpClientO); // ・・・(略)・・・
「googleapis_auth」を使うときに必要だった「url_launcher」パッケージを用いてブラウザを起動するコードなどが不要なので楽。
ただ、1つ難点があるのは、この方法だとアクセストークンがリフレッシュされないため、1時間経つとトークンが失効し、再度サインインが必要になってしまう点。
この問題を解消するには、下記説明ページを参考に、エンドポイント(endpoint)のURIを用意して、「register」メソッドの「exchangeEndpoint:」プロパティに設定する必要があるようだが、方法が難解で理解が及ばず。。。
自分のアプリでは、Google Driveとの間でのデータのバックアップ・リストアが目的だったので、サインインの維持は必要ないと判断し、エンドポイントの設定は割愛することとした。
「googleapis_auth」を用いたサインインも復活(セキュリティソフトの問題)
なぜ従前の「googleapis_auth」を用いる方法が使えなくなったのか、再度調べてみた。
すると、こちらのDart公式説明ページにおいて、「http://localhost:8080」を使用する、と書かれている事に気づく。
そこで、ローカルホスト関連のエラーについて調べたところ、以下の情報が見つかった。
いずれも「ESET(セキュリティソフト)のファイアウォールを無効化したらつながった」とのこと。
そこで、自分のMacに入れているのセキュリティソフトを無効化したところ、なんとアクセスできるようになった。。
ちょうどエラーが出るようになったタイミングで、当該セキュリティソフトの再インストール・アップデートをしており、それが影響したのかもしれない。。
まさかセキュリティソフトが原因とは。。
ただ、「google_sign_in_dartio」を用いる方法は、「googleapis_auth」を使う方法に比べ、実装の手間が少ないので、引き続き前者を採用しようと思う。
\一般的なエラー対処法をまとめた記事はこちら/
リリースしたアプリ(全てFlutterで開発)
個人アプリ開発で役立ったもの
おすすめの学習教材
\超初心者向けでオススメな元Udemyの講座/
\キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/
\Gitの基礎について無料で学べる/
おすすめの学習書籍
\実用的。image_pickerに関してかなり助けられた/
\Dartの基礎文法を素早くインプットできる/