Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[google_sign_in] Support Dart-based configuration and serverClientId #5250

Merged
Merged
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
5 changes: 5 additions & 0 deletions packages/google_sign_in/google_sign_in/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
16 changes: 16 additions & 0 deletions packages/google_sign_in/google_sign_in/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ This plugin requires iOS 9.0 or higher.
<!-- End of the Google Sign-in Section -->
```

As an alternative to adding `GoogleService-Info.plist` to your Xcode project, you can instead
cyanglaz marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
37 changes: 31 additions & 6 deletions packages/google_sign_in/google_sign_in/lib/google_sign_in.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -183,6 +184,7 @@ class GoogleSignIn {
this.scopes = const <String>[],
this.hostedDomain,
this.clientId,
this.serverClientId,
});

/// Factory for creating default sign in user experience.
Expand Down Expand Up @@ -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<GoogleSignInAccount?> _currentUserController =
StreamController<GoogleSignInAccount?>.broadcast();

Expand Down Expand Up @@ -260,15 +282,18 @@ class GoogleSignIn {
}

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.
Expand Down
8 changes: 4 additions & 4 deletions packages/google_sign_in/google_sign_in/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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();

Expand Down Expand Up @@ -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<Exception>()));

when(mockPlatform.init()).thenAnswer((Invocation _) async {});
when(mockPlatform.initWithParams(any))
.thenAnswer((Invocation _) async {});
expect(await googleSignIn.signIn(), isNotNull);
});

Expand Down Expand Up @@ -334,13 +348,44 @@ void main() {

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,
),
)));
}