1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Google認証のパッケージで "BREAKING CHANGE"、ってことで今のうち直した話

Last updated at Posted at 2025-07-09

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が上がっていました。(起票者の実装がないので何ともいえませんが・・・)

https://github.com/flutter/flutter/issues/174681

今回のゴール

私が作成中のアプリは、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メソッド
ユーザーにスコープの認可を要求します。

こういった画面です。

許可を求める画面(スマホ)

masking_認可画面.png

処理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ログイン」をタップして、アカウントを選択するモーダルが表示されます。

通常の認証時(画面)

masking_アカウント選択画面.png

あるアカウントでサインイン歴がある状態

ログイン画面を表示して、特に何もせずこの状態になります。

「サインイン」をタップすると、1秒前後「ログイン中」と表示された後、アプリの画面が表示されました。

attemptLightweightAuthentication利用時

masking_軽量認証画面.png

masking_ログイン処理中画面.png

実装

最初に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より前で動かない話)
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?