Google認証に使うパッケージ
2025年6月24日に、
google_sign_in
パッケージのメジャーバージョンが、7
になりました。
Flutterで認証機能の構築経験がある方は、ご存知のパッケージかと思います。
changelog に、BREAKING CHANGEと記載されていました。
BREAKING CHANGE: Many APIs have changed or been replaced to reflect the current APIs and best practices of the underlying platform SDKs. For full details, see the README and migration guide, but notable highlights include:
・The GoogleSignIn instance is now a singleton.
・Clients must call and await the new initialize method before calling any other methods on the instance.
・Authentication and authorization are now separate steps.
・Access tokens and server auth codes are obtained via separate calls.
- GoogleSignInインスタンスがシングルトンに
- クライアントは、インスタンスの他のメソッドを呼び出す前に、新しいinitializeメソッドを呼び出してawaitする必要あり
- 認証と認可が別々のステップに
- アクセストークンとサーバー認証コードは、別々の呼び出しで取得
今回の記事ですが、
シングルトンの話とサーバー認証コード、以外の話は記載しています。
ログインとサインイン
両方の単語を使っておりますが、この記事内では同じ意味として扱わせていただきます。ご了承ください。
リポジトリで情報収集
README.md
MIGRATION.md
バージョン6
から7
でどういった変更があったか記載されています。これを元に必要な作業を理解します。
実装例
アプリによって構成が異なるので参考程度ですが、実装例が載っていました。
この実装通り進めた結果、Android14より昔のバージョンでは思ったように機能しない、
といったIssueが上がっていました。(起票者の実装がないので何ともいえませんが・・・)
今回のゴール
私が作成中のアプリは、Googleアカウント認証を利用して、Supabaseプロジェクトと連携する形になっています。
コードを修正して、上記の構成を維持できるようにします。
Supabaseについては、以下をご参照ください。
実装
実装して確認する他ないので進めます!
コードをベースに違いを見ていきます。
ちなみに実装時、
私のFlutterのバージョンは、3.32.2
です。
注意事項
以下の話は記載していません。
- Dartの文法的な話
- Supabaseの話
- clientIdとserverClientIdの取得方法
私の記事で恐縮ですが、別記事の clientIdとserverClientIdの設定 で記載しています。
流れを理解いただく上での最低限のコードであり、以下内容は省いています。ご了承ください。
- import文の情報
- メソッドを用いた書き方
- 細かいエラーハンドリング
これまで(メジャーバージョン6
まで)
// 初期化
final GoogleSignIn _googleSignIn = GoogleSignIn(
clientId: 'googleClientId',
serverClientId: 'googleWebServerClientId',
);
final supabase = Supabase.instance.client;
// サインイン処理
// 1.認証
final googleUser = await _googleSignIn.signIn();
// 2.トークン情報取得
final googleAuth = await googleUser!.authentication;
final accessToken = googleAuth.accessToken;
final idToken = googleAuth.idToken;
// 3.supabaseへのサインイン
await supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: idToken,
accessToken: accessToken,
);
これから(メジャーバージョン7
から)
// 0.初期化
await GoogleSignIn.instance.initialize(
clientId: 'googleClientId',
serverClientId: 'googleWebServerClientId',
);
final supabase = Supabase.instance.client;
// サインイン処理
// 1.プラットフォームの認証機能対応の確認
if (!_googleSignIn.supportsAuthenticate()) {
throw Exception('Authenticate not supported on this platform');
}
// 2.認証処理
final googleUser = await _googleSignIn.authenticate();
// 3. スコープ定義
const List<String> scopes = <String>[
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid',
];
// 4. 認可処理
var authorization = await googleUser.authorizationClient.authorizationForScopes(scopes);
// If we don't have authorization, request it
if (authorization == null) {
final authorizeResult = await googleUser.authorizationClient.authorizeScopes(scopes);
if (authorizeResult.accessToken.isEmpty) {
throw Exception('Failed to get authorization after user granted it');
}
authorization = authorizeResult;
}
// 5.トークン情報取得(3の結果を利用)
final googleAuth = googleUser.authentication;
final idToken = googleAuth.idToken;
final accessToken = authorization.accessToken;
// 6.supabaseへのサインイン(特に変わりなし)
await supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: idToken,
accessToken: accessToken,
);
0. 初期化
changelog より
Clients must call and await the new initialize method before calling any other methods on the instance.
initialize
メソッド
いきなりサインインは呼び出しができないので、まずinitialize
メソッドでインスタンスを利用可能な状態にします。
1. プラットフォームの認証機能対応の確認
MIGRATION.md より
The new supportsAuthenticate method allows clients to determine at runtime whether the authenticate method is supported, as some platforms do not allow custom UI to trigger explicit authentication. These platforms instead provide some other platform-specific way of triggering authentication. As of publishing, the only platform that does not support authenticate is web, where google_sign_in_web's renderButton is used to create a sign-in button.
supportsAuthenticate
メソッド
クライアントは実行時に authenticate
メソッドがサポートされているか判定する必要があるそうです。
「一部のプラットフォームでは、明示的な認証をトリガーするカスタムUIが許可されていないため」と記載されています。
また、Webはまだサポートされていないようです。
2. 認証
MIGRATION.md より
authenticate replaces the authentication portion of signIn on platforms that support it (see below).
authenticate
メソッド
これまでのバージョンにおけるsignIn
メソッドです。
3. スコープ定義
changelog より
Authentication and authorization are now separate steps.
認証と認可が別処理になりました。
これからの実装にはscope
が登場し、「認可」に関する処理が必要です。
Scope一覧は以下で確認できます。
Google認証機能構築で必要となるscopeは、以下の3つでした。
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
openid
4. 認可処理
MIGRATION.md より
Client authorization is handled via two new methods:
authorizationForScopes, which returns an access token if the requested scopes are already authorized, or null if not, and
authorizeScopes, which requests that the user authorize the scopes, and is expected to show UI.Clients should generally attempt to get tokens via authorizationForScopes, and if they are unable to do so, show some UI to request authoriaztion that calls authorizeScopes. This is similar to the previously web-only flow of calling canAccessScopes and then calling addScopes if necessary.
以下2つのメソッドでについて記載されています。
authorizationForScopes
メソッド
状態に応じて返す値が変わります。
- 認可済:アクセストークン情報
- 未認可:null
authorizeScopes
メソッド
ユーザーにスコープの認可を要求します。
こういった画面です。
処理5、処理6
これまでと同じなので、説明は省きます。
ここまでできれば、一旦完了です。
まとめ(当初のゴールは達成)
当初ゴールに設定した、Supabaseとの連携も問題なく行えました。
認証関連のBREAKING CHANGEだったので、ちょっと焦りましたが、生成AIと会話もしながら(私はClaude Desktopを頻繁に利用しています)
比較的短い時間(3時間前後)で移行することができました。
実はもうちょっと続きがありまして、
attemptLightweightAuthentication
メソッドにもふれました。
ついでにやってみた話ですので、延長戦の扱いです。
気になる方はご覧ください!
attemptLightweightAuthenticationも導入
MIGRATION.md より
signInSilently has been replaced with attemptLightweightAuthentication. The intended usage is essentially the same, but the change reflects that it is no longer guaranteed to be silent. For example, as of the publishing of 7.0, on web this may show a floating sign-in card, and on Android it may show an account selection sheet.
This new method is no longer guaranteed to return a future. This allows clients to distinguish, at runtime:
platforms where a definitive "signed in" or "not signed in" response can be returned quickly, and thus await-ing completion is reasonable, in which case a Future is returned, and
platforms (such as web) where it could take an arbitrary amount of time, in which case no Future is returned, and clients should assume a non-signed-in state until/unless a sign-in event is eventually posted to the authenticationEvents stream.
日本語にすると以下の通りですが、このあたりの話は少し高度な内容と受け取りました。
プラットフォームに応じて適切な形で実装に組み込む必要がありそうです。
この新しいメソッドは、もはやFutureを返すことが保証されていません。これにより、クライアントは実行時に以下を区別できます:
明確な「サインイン済み」または「サインインしていない」レスポンスを素早く返すことができるプラットフォーム。この場合、完了をawaitすることが妥当であり、Futureが返されます。
任意の時間がかかる可能性のあるプラットフォーム(Webなど)。この場合、Futureは返されず、クライアントは最終的にサインインイベントがauthenticationEventsストリームに投稿されるまで/されない限り、非サインイン状態であると仮定する必要があります。
私の場合は、Supabaseの認証済ユーザーが存在しない限りは、attemptLightweightAuthenticationを呼び出す形にしました。
いずれのアカウントでも未サインイン状態
特にどのアカウントログインしていないときは、画面上の「Googleログイン」をタップして、アカウントを選択するモーダルが表示されます。
あるアカウントでサインイン歴がある状態
ログイン画面を表示して、特に何もせずこの状態になります。
「サインイン」をタップすると、1秒前後「ログイン中」と表示された後、アプリの画面が表示されました。
実装
最初にattemptLightweightAuthentication
メソッドを呼び出すところは本題で記載した内容と異なりますが、
その先Supabaseでログインできるようにするまでの流れは同じでした。
本題の実装ができていれば、こちらもそれほど大変ではない、と感じました。
final googleUser = await _googleSignIn.attemptLightweightAuthentication();
const List<String> scopes = <String>[
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid',
];
var authorization = await googleUser.authorizationClient.authorizationForScopes(scopes);
// 以降同じ処理
更新日 | 内容 |
---|---|
2025/08/29 | Issue情報を追加。(google_signinの実装例を使うと、Android14より前で動かない話) |