Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/firebase_ui_auth/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
google_sign_in: ^6.2.1
google_sign_in: ^7.1.0
http: ^1.1.2
integration_test:
sdk: flutter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class GoogleSignInButton extends _GoogleSignInButton {
super.key,
required super.loadingIndicator,
required super.clientId,
super.serverClientId,
super.redirectUri,
super.scopes,
super.action = null,
Expand All @@ -36,6 +37,7 @@ class GoogleSignInIconButton extends _GoogleSignInButton {
const GoogleSignInIconButton({
super.key,
required super.clientId,
super.serverClientId,
required super.loadingIndicator,
super.scopes,
super.action = null,
Expand Down Expand Up @@ -75,6 +77,7 @@ class _GoogleSignInButton extends StatelessWidget {
final SignedInCallback? onSignedIn;
final double size;
final String clientId;
final String? serverClientId;
final String? redirectUri;
final List<String>? scopes;
final void Function(Exception exception)? onError;
Expand All @@ -83,6 +86,7 @@ class _GoogleSignInButton extends StatelessWidget {
const _GoogleSignInButton({
super.key,
required this.clientId,
this.serverClientId,
required this.loadingIndicator,
this.scopes,
String? label,
Expand All @@ -106,6 +110,7 @@ class _GoogleSignInButton extends StatelessWidget {

return GoogleProvider(
clientId: clientId,
serverClientId: serverClientId,
redirectUri: redirectUri,
scopes: scopes ?? [],
);
Expand Down
100 changes: 76 additions & 24 deletions packages/firebase_ui_oauth_google/lib/src/provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ class GoogleProvider extends OAuthProvider {
/// Ignored on Android and iOS (if `iOSPreferPlist` is true).
final String clientId;

/// The client ID of the web OAuth client associated with the app's
/// server-side component, if any.
///
/// On Android this is required to receive an ID token, unless the app uses
/// `google-services.json` and it contains a web OAuth client entry, in
/// which case the plugin reads the value from there.
final String? serverClientId;

/// When true, the Google Sign In plugin will use the GoogleService-Info.plist
/// for configuration instead of the `clientId` parameter.
final bool iOSPreferPlist;
Expand All @@ -28,7 +36,23 @@ class GoogleProvider extends OAuthProvider {
/// The list of requested authorization scopes requested when signing in.
final List<String>? scopes;

late GoogleSignIn provider;
/// The plugin instance. google_sign_in 7 exposes a single shared instance.
/// Assignable so that tests can inject a mock.
GoogleSignIn provider = GoogleSignIn.instance;

// google_sign_in 7 requires initialize to be called exactly once, while
// multiple GoogleProvider instances may be created (for example one per
// GoogleSignInButton). The first instance to sign in configures the plugin.
static Future<void>? _initialization;
Comment thread
demolaf marked this conversation as resolved.

/// Resets the one-time initialization state.
///
/// The plugin is initialized exactly once per process, so tests that inject
/// a fresh mock in `setUp` must clear the cached future to stay isolated.
@visibleForTesting
static void debugReset() {
_initialization = null;
}

@override
final fba.GoogleAuthProvider firebaseAuthProvider = fba.GoogleAuthProvider();
Expand All @@ -44,19 +68,14 @@ class GoogleProvider extends OAuthProvider {

GoogleProvider({
required this.clientId,
this.serverClientId,
this.redirectUri,
this.scopes,
this.iOSPreferPlist = false,
}) {
firebaseAuthProvider.setCustomParameters(const {
'prompt': 'select_account',
});

if (_ignoreClientId()) {
provider = GoogleSignIn(scopes: scopes ?? []);
} else {
provider = GoogleSignIn(clientId: clientId, scopes: scopes ?? []);
}
}

bool _ignoreClientId() {
Expand All @@ -68,25 +87,57 @@ class GoogleProvider extends OAuthProvider {
return false;
}

Future<void> _ensureInitialized() {
return _initialization ??= provider
.initialize(
clientId: _ignoreClientId() ? null : clientId,
serverClientId: serverClientId,
)
.catchError((Object err) {
// Allow a later sign-in attempt to retry initialization instead of
// rethrowing the same stale error forever.
_initialization = null;
throw err;
});
}
Comment thread
demolaf marked this conversation as resolved.

@override
void mobileSignIn(AuthAction action) async {
provider
.signIn()
.then((user) {
if (user == null) throw AuthCancelledException();
return user.authentication;
})
.then((auth) {
final credential = fba.GoogleAuthProvider.credential(
accessToken: auth.accessToken,
idToken: auth.idToken,
);

onCredentialReceived(credential, action);
})
.catchError((err) {
authListener.onError(err);
});
final requestedScopes = scopes ?? const <String>[];

try {
await _ensureInitialized();

final account = await provider.authenticate(scopeHint: requestedScopes);

// Authentication and authorization are separate steps in
// google_sign_in 7. Reuse an existing authorization when one is
// available, otherwise prompt for the requested scopes so that the
// credential carries an access token, matching the previous behavior.
String? accessToken;
if (requestedScopes.isNotEmpty) {
final client = account.authorizationClient;
final authorization =
await client.authorizationForScopes(requestedScopes) ??
await client.authorizeScopes(requestedScopes);
accessToken = authorization.accessToken;
}

final credential = fba.GoogleAuthProvider.credential(
accessToken: accessToken,
idToken: account.authentication.idToken,
);

onCredentialReceived(credential, action);
} on GoogleSignInException catch (err) {
if (err.code == GoogleSignInExceptionCode.canceled) {
authListener.onError(AuthCancelledException());
} else {
authListener.onError(err);
}
} catch (err) {
authListener.onError(err);
}
}

@override
Expand All @@ -112,6 +163,7 @@ class GoogleProvider extends OAuthProvider {
if (defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
await _ensureInitialized();
await provider.signOut();
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/firebase_ui_oauth_google/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies:
firebase_ui_oauth: ^2.1.0
flutter:
sdk: flutter
google_sign_in: ^6.2.1
google_sign_in: ^7.1.0

dev_dependencies:
flutter_test:
Expand Down
Loading
Loading