Skip to content

[google_sign_in] Redesign API for current identity SDKs #9267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 47 commits into
base: main
Choose a base branch
from

Conversation

stuartmorgan-g
Copy link
Contributor

This is a full overhaul of the google_sign_in API, with breaking changes for all component packages—including the platform interface. The usual model of adding the new approach while keeping the old one is not viable here, as the underlying SDKs have changed significantly since the original API was designed. Web already had some only-partially-compatible shims for this reason, and Android would have had to do something similar; see flutter/flutter#119300 and flutter/flutter#154205, and the design doc for more background.

Pre-Review Checklist

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2 3

Copy link
Contributor

@camsim99 camsim99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Android implementation!

Future<PlatformGoogleIdTokenCredential?> _authenticate({
required bool filterToAuthorized,
required bool autoSelectEnabled,
required bool useButtonFlow,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if useButtonFlow is true, then basically the other options are ignored? Would it be helpful to warn users here about that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a wrapper class at this layer and the Pigeon layer that encapsulate the options that are specific to the non-button flow, and also added some docs, to make things clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reworked the API here and at the Pigeon layer to put the options specific to non-button-flow in a class, and added comments, to make it clearer what doesn't apply. (When I first wrote this code I didn't use the button flow, so the flat arguments made sense.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool this is much clearer, thank you!

ResultCompat.asCompatCallback(
reply -> {
// This is never called, since this test doesn't trigger the getCredentialsAsync callback.
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: could throw an exception to ensure it's not called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added fail() to all of these, and adjusted the comment slightly to explain why.

@@ -13,3 +13,28 @@ should add it to your `pubspec.yaml` as usual.

[1]: https://pub.dev/packages/google_sign_in
[2]: https://flutter.dev/to/endorsed-federated-plugin

## Integration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we consider linking to the credentials docs on this? https://developer.android.com/identity/sign-in/credential-manager-siwg#set-google for the non-firebase option? If it's more confusing than helpful because some steps don't apply, maybe not though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the time I was updating the READMEs I was so used to skipping down to the code on that page I forgot it had setup instructions! I've adjusted to link to that as the non-Firebase option, and removed some of the details since they are covered more thoroughly there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hahaha totally understandable! LGTM!

@stuartmorgan-g stuartmorgan-g requested a review from ash2moon as a code owner May 29, 2025 02:07
@stuartmorgan-g stuartmorgan-g requested review from camsim99 and ditman May 29, 2025 02:07
@stuartmorgan-g stuartmorgan-g added the triage-ios Should be looked at in iOS triage label May 29, 2025
Copy link
Contributor

@camsim99 camsim99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Android implementation LGTM!

Future<PlatformGoogleIdTokenCredential?> _authenticate({
required bool filterToAuthorized,
required bool autoSelectEnabled,
required bool useButtonFlow,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool this is much clearer, thank you!

@@ -13,3 +13,28 @@ should add it to your `pubspec.yaml` as usual.

[1]: https://pub.dev/packages/google_sign_in
[2]: https://flutter.dev/to/endorsed-federated-plugin

## Integration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hahaha totally understandable! LGTM!

@stuartmorgan-g
Copy link
Contributor Author

@LongCatIsLooong / @cbracken Ping on the iOS portion of this review.

@stuartmorgan-g
Copy link
Contributor Author

@bparrishMines Could you review the platform interface and app-facing package changes?

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone that's new to this plugin, thanks for the design doc! I still have a few questions after reading the doc.


### Requesting more scopes when needed

If an app determines that the user hasn't granted the scopes it requires, it
should initiate an Authorization request. (Remember that in the web platform,
this request **must be initiated from an user interaction**, like a button press).
should initiate an Authorization request. On some platforms, such as web,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this sounds like there are platforms other than web where user interactions are required for Authorization request. Is that the case? How do I know which of the platforms my app targets have such restriction?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supportsAuthenticate? But that's for authentication I assume?

}
// #enddocregion CanAccessScopes
// #docregion Setup
final GoogleSignIn signIn = GoogleSignIn.instance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this reminds me of flutter/flutter#168100, if the user connects / disconnects a mouse / keyboard on Android the activity will restart and that would result in the element tree being re-inflated anew.

I guess that does not break the new flow (from reading the docs initialize has to be called exactly once in a program) since the new activity will spin up a new dart vm? Still it would be an annoyance that every time I unplug my keyboard I have to go over the sign-in process again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or when the activity restarts, the lightweight authentication path will be taken and the flow will typically be invisible to user / require no additional user interactions?

EDIT: Just read the design doc and it mentioned "Fully silent sign in is no longer available"

Future<void> _handleAuthenticationEvent(
GoogleSignInAuthenticationEvent event) async {
// #docregion CheckAuthorization
GoogleSignInAccount? user;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uber nit, consider making user and authorization final if possible?

///
/// Returned Future resolves to an instance of [GoogleSignInAccount] for a
/// successful sign in or `null` in case sign in process was aborted.
/// If this returns false, [authenticate] will throw an UnsupportedError if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing backticks / square brackets around UnsupportedError?

/// successful sign in or `null` in case sign in process was aborted.
/// If this returns false, [authenticate] will throw an UnsupportedError if
/// called. See the platform-specific documentation for the package to
/// determine how authentication his handled. For instance, the platform may
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

his -> is


/// A base class for authentication event streams.
@immutable
sealed class GoogleSignInAuthenticationEvent {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these classes needed? It seems the sealed type is a glorified GoogleSignInAccount?.


// The plugin registrar, for querying views.
@property(strong, nonnull) id<FlutterPluginRegistrar> registrar;
@property(nonatomic) id<FlutterPluginRegistrar> registrar;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this still nonnull?

completion:(nullable void (^)(GIDSignInResult *_Nullable signInResult,
NSError *_Nullable error))completion {
GIDGoogleUser *currentUser = self.signIn.currentUser;
forGoogleSignInUser:(GIDGoogleUser *)user
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the user argument is not nullable, otherwise it's failing silently?

}];
}

- (void)addScopes:(nonnull NSArray<NSString *> *)scopes
forUser:(NSString *)userId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can userId be null?

- (void)signOutWithError:(FlutterError *_Nullable *_Nonnull)error {
[self.signIn signOut];
[self.usersByIdentifier removeAllObjects];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signOut method seems to only sign out the current user: https://developers.google.com/identity/sign-in/ios/reference/Classes/GIDSignIn

Why do we have to remove everything in the dictionary? If refreshedAuthorizationTokensForUser gets called for a different user (is that possible`?) the app would get an error it seems?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
override: allow breaking change Override the check preventing breaking changes to platform interfaces p: google_sign_in platform-android platform-ios platform-macos platform-web triage-ios Should be looked at in iOS triage
Projects
None yet
5 participants