diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index d3f0bdaea17e..7e433a71368f 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 5ede3be051b2..e467ca8541b9 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 @@ This plugin requires iOS 9.0 or higher. ``` +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 3c62e0e1a655..a1f8f7bc49ca 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 @@ import 'src/common.dart'; 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 @@ class GoogleSignIn { this.scopes = const [], this.hostedDomain, this.clientId, + this.serverClientId, }); /// Factory for creating default sign in user experience. @@ -228,9 +230,29 @@ class GoogleSignIn { /// 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 _currentUserController = StreamController.broadcast(); @@ -260,15 +282,18 @@ class GoogleSignIn { } Future _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 9ea09dd1aeb4..c7724adcebad 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 @@ description: Flutter plugin for Google Sign-In, a secure authentication system 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 @@ flutter: 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 2bc51b63d111..b8676bda298e 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/google_sign_in.dart'; 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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())); - when(mockPlatform.init()).thenAnswer((Invocation _) async {}); + when(mockPlatform.initWithParams(any)) + .thenAnswer((Invocation _) async {}); expect(await googleSignIn.signIn(), isNotNull); }); @@ -334,13 +348,44 @@ void main() { void _verifyInit( MockGoogleSignInPlatform mockSignIn, { + List scopes = const [], SignInOption signInOption = SignInOption.standard, + String? hostedDomain, String? clientId, + String? serverClientId, + bool forceCodeForRefreshToken = false, }) { - verify(mockSignIn.init( - signInOption: signInOption, - scopes: [], - hostedDomain: null, - clientId: clientId, - )); + verify(mockSignIn.initWithParams(argThat( + isA() + .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, + ), + ))); }