Skip to content

Commit

Permalink
[google_sign_in] Support Dart-based configuration and serverClientId (
Browse files Browse the repository at this point in the history
  • Loading branch information
blaugold committed Jul 7, 2022
1 parent 569268f commit d07de93
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 19 deletions.
5 changes: 5 additions & 0 deletions 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.
Expand Down
16 changes: 16 additions & 0 deletions packages/google_sign_in/google_sign_in/README.md
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
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
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
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
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,
),
)));
}

0 comments on commit d07de93

Please sign in to comment.