[google_sign_in] Support Dart-based configuration and `serverClientId` (#5250)
diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md
index d3f0bda..7e433a7 100644
--- a/packages/google_sign_in/google_sign_in/CHANGELOG.md
+++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 5.4.0
+
+* Adds support for configuring `serverClientId` through `GoogleSignIn` constructor.
+* Adds support for Dart-based configuration as alternative to `GoogleService-Info.plist` for iOS.
+
## 5.3.3
* Updates references to the obsolete master branch.
diff --git a/packages/google_sign_in/google_sign_in/README.md b/packages/google_sign_in/google_sign_in/README.md
index 5ede3be..e467ca8 100644
--- a/packages/google_sign_in/google_sign_in/README.md
+++ b/packages/google_sign_in/google_sign_in/README.md
@@ -65,6 +65,22 @@
<!-- End of the Google Sign-in Section -->
```
+As an alternative to adding `GoogleService-Info.plist` to your Xcode project, you can instead
+configure your app in Dart code. In this case, skip steps 3-6 and pass `clientId` and
+`serverClientId` to the `GoogleSignIn` constructor:
+
+```dart
+GoogleSignIn _googleSignIn = GoogleSignIn(
+ ...
+ // The OAuth client id of your app. This is required.
+ clientId: ...,
+ // If you need to authenticate to a backend server, specify its OAuth client. This is optional.
+ serverClientId: ...,
+);
+```
+
+Note that step 7 is still required.
+
#### iOS additional requirement
Note that according to
diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart
index 3c62e0e..a1f8f7b 100644
--- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart
+++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart
@@ -12,6 +12,7 @@
export 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'
show SignInOption;
+
export 'src/common.dart';
export 'widgets.dart';
@@ -183,6 +184,7 @@
this.scopes = const <String>[],
this.hostedDomain,
this.clientId,
+ this.serverClientId,
});
/// Factory for creating default sign in user experience.
@@ -228,9 +230,29 @@
/// Domain to restrict sign-in to.
final String? hostedDomain;
- /// Client ID being used to connect to google sign-in. Only supported on web.
+ /// Client ID being used to connect to google sign-in.
+ ///
+ /// This option is not supported on all platforms (e.g. Android). It is
+ /// optional if file-based configuration is used.
+ ///
+ /// The value specified here has precedence over a value from a configuration
+ /// file.
final String? clientId;
+ /// Client ID of the backend server to which the app needs to authenticate
+ /// itself.
+ ///
+ /// Optional and not supported on all platforms (e.g. web). By default, it
+ /// is initialized from a configuration file if available.
+ ///
+ /// The value specified here has precedence over a value from a configuration
+ /// file.
+ ///
+ /// [GoogleSignInAuthentication.idToken] and
+ /// [GoogleSignInAccount.serverAuthCode] will be specific to the backend
+ /// server.
+ final String? serverClientId;
+
final StreamController<GoogleSignInAccount?> _currentUserController =
StreamController<GoogleSignInAccount?>.broadcast();
@@ -260,15 +282,18 @@
}
Future<void> _ensureInitialized() {
- return _initialization ??= GoogleSignInPlatform.instance.init(
+ return _initialization ??=
+ GoogleSignInPlatform.instance.initWithParams(SignInInitParameters(
signInOption: signInOption,
scopes: scopes,
hostedDomain: hostedDomain,
clientId: clientId,
- )..catchError((dynamic _) {
- // Invalidate initialization if it errors out.
- _initialization = null;
- });
+ serverClientId: serverClientId,
+ ))
+ ..catchError((dynamic _) {
+ // Invalidate initialization if it errors out.
+ _initialization = null;
+ });
}
/// The most recently scheduled method call.
diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml
index 9ea09dd..c7724ad 100644
--- a/packages/google_sign_in/google_sign_in/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in/pubspec.yaml
@@ -3,7 +3,7 @@
for signing in with a Google account on Android and iOS.
repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
-version: 5.3.3
+version: 5.4.0
environment:
@@ -23,9 +23,9 @@
dependencies:
flutter:
sdk: flutter
- google_sign_in_android: ^5.2.5
- google_sign_in_ios: ^5.2.5
- google_sign_in_platform_interface: ^2.1.0
+ google_sign_in_android: ^6.0.0
+ google_sign_in_ios: ^5.4.0
+ google_sign_in_platform_interface: ^2.2.0
google_sign_in_web: ^0.10.0
dev_dependencies:
diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart
index 2bc51b6..b8676bd 100644
--- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart
+++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart
@@ -9,6 +9,7 @@
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
+
import 'google_sign_in_test.mocks.dart';
/// Verify that [GoogleSignInAccount] can be mocked even though it's unused
@@ -58,7 +59,7 @@
verify(mockPlatform.signIn());
});
- test('signIn prioritize clientId parameter when available', () async {
+ test('clientId parameter is forwarded to implementation', () async {
const String fakeClientId = 'fakeClientId';
final GoogleSignIn googleSignIn = GoogleSignIn(clientId: fakeClientId);
@@ -68,6 +69,17 @@
verify(mockPlatform.signIn());
});
+ test('serverClientId parameter is forwarded to implementation', () async {
+ const String fakeServerClientId = 'fakeServerClientId';
+ final GoogleSignIn googleSignIn =
+ GoogleSignIn(serverClientId: fakeServerClientId);
+
+ await googleSignIn.signIn();
+
+ _verifyInit(mockPlatform, serverClientId: fakeServerClientId);
+ verify(mockPlatform.signIn());
+ });
+
test('signOut', () async {
final GoogleSignIn googleSignIn = GoogleSignIn();
@@ -240,10 +252,12 @@
test('can sign in after init failed before', () async {
final GoogleSignIn googleSignIn = GoogleSignIn();
- when(mockPlatform.init()).thenThrow(Exception('First init fails'));
+ when(mockPlatform.initWithParams(any))
+ .thenThrow(Exception('First init fails'));
expect(googleSignIn.signIn(), throwsA(isInstanceOf<Exception>()));
- when(mockPlatform.init()).thenAnswer((Invocation _) async {});
+ when(mockPlatform.initWithParams(any))
+ .thenAnswer((Invocation _) async {});
expect(await googleSignIn.signIn(), isNotNull);
});
@@ -334,13 +348,44 @@
void _verifyInit(
MockGoogleSignInPlatform mockSignIn, {
+ List<String> scopes = const <String>[],
SignInOption signInOption = SignInOption.standard,
+ String? hostedDomain,
String? clientId,
+ String? serverClientId,
+ bool forceCodeForRefreshToken = false,
}) {
- verify(mockSignIn.init(
- signInOption: signInOption,
- scopes: <String>[],
- hostedDomain: null,
- clientId: clientId,
- ));
+ verify(mockSignIn.initWithParams(argThat(
+ isA<SignInInitParameters>()
+ .having(
+ (SignInInitParameters p) => p.scopes,
+ 'scopes',
+ scopes,
+ )
+ .having(
+ (SignInInitParameters p) => p.signInOption,
+ 'signInOption',
+ signInOption,
+ )
+ .having(
+ (SignInInitParameters p) => p.hostedDomain,
+ 'hostedDomain',
+ hostedDomain,
+ )
+ .having(
+ (SignInInitParameters p) => p.clientId,
+ 'clientId',
+ clientId,
+ )
+ .having(
+ (SignInInitParameters p) => p.serverClientId,
+ 'serverClientId',
+ serverClientId,
+ )
+ .having(
+ (SignInInitParameters p) => p.forceCodeForRefreshToken,
+ 'forceCodeForRefreshToken',
+ forceCodeForRefreshToken,
+ ),
+ )));
}