Skip to content

Commit ae64c4e

Browse files
[extension_google_sign_in_as_googleapis_auth] Update to google_sign_in 7 (#9484)
Updates the extension to work with the newly released `google_sign_in` 7.x, which reworked the API surface. The extension method can no longer do as many of the steps, so its utility is lower. Longer term this may not be worth maintaining as a package, but for now this will simplify migration to `google_sign_in` 7.x for existing clients of this package. Fixes flutter/flutter#171048 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 2c8d2bc commit ae64c4e

File tree

7 files changed

+183
-140
lines changed

7 files changed

+183
-140
lines changed

packages/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
## NEXT
2-
1+
## 3.0.0
2+
3+
* **BREAKING CHANGES**:
4+
* The extension method is now on `GoogleSignInClientAuthorization` instead of
5+
`GoogleSignIn`, so it must be used after completing an authorization flow.
6+
* The extension method has been renamed to `authClient`.
7+
* The extension method now requires passing `scopes`, matching those used to
8+
request the authorization.
39
* Updates minimum supported SDK version to Flutter 3.27/Dart 3.6.
410

511
## 2.0.13

packages/extension_google_sign_in_as_googleapis_auth/README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ That object can then be used to create instances of `googleapis` API clients:
1919

2020
<?code-excerpt "example/lib/main.dart (CreateAPIClient)"?>
2121
```dart
22-
// Retrieve an [auth.AuthClient] from the current [GoogleSignIn] instance.
23-
final auth.AuthClient? client = await _googleSignIn.authenticatedClient();
24-
25-
assert(client != null, 'Authenticated client missing!');
26-
27-
// Prepare a People Service authenticated client.
28-
final PeopleServiceApi peopleApi = PeopleServiceApi(client!);
29-
// Retrieve a list of the `names` of my `connections`
30-
final ListConnectionsResponse response =
31-
await peopleApi.people.connections.list(
32-
'people/me',
33-
personFields: 'names',
34-
);
22+
import 'package:googleapis_auth/googleapis_auth.dart' as auth show AuthClient;
23+
// ···
24+
// Retrieve an [auth.AuthClient] from a GoogleSignInClientAuthorization.
25+
final auth.AuthClient client = authorization.authClient(scopes: scopes);
26+
27+
// Prepare a People Service authenticated client.
28+
final PeopleServiceApi peopleApi = PeopleServiceApi(client);
29+
// Retrieve a list of connected contacts' names.
30+
final ListConnectionsResponse response =
31+
await peopleApi.people.connections.list(
32+
'people/me',
33+
personFields: 'names',
34+
);
3535
```
3636

3737
## Example

packages/extension_google_sign_in_as_googleapis_auth/example/lib/main.dart

Lines changed: 132 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sig
1010
import 'package:flutter/material.dart';
1111
import 'package:google_sign_in/google_sign_in.dart';
1212
import 'package:googleapis/people/v1.dart';
13+
// #docregion CreateAPIClient
1314
import 'package:googleapis_auth/googleapis_auth.dart' as auth show AuthClient;
15+
// #enddocregion CreateAPIClient
1416

15-
final GoogleSignIn _googleSignIn = GoogleSignIn(
16-
// Optional clientId
17-
// clientId: '[YOUR_OAUTH_2_CLIENT_ID]',
18-
scopes: <String>[PeopleServiceApi.contactsReadonlyScope],
19-
);
17+
/// The scopes used by this example.
18+
const List<String> scopes = <String>[PeopleServiceApi.contactsReadonlyScope];
2019

2120
void main() {
2221
runApp(
@@ -38,37 +37,84 @@ class SignInDemo extends StatefulWidget {
3837

3938
/// The state of the main widget.
4039
class SignInDemoState extends State<SignInDemo> {
40+
late Future<void> _signInInitialized;
4141
GoogleSignInAccount? _currentUser;
42+
GoogleSignInClientAuthorization? _authorization;
4243
String _contactText = '';
4344

4445
@override
4546
void initState() {
4647
super.initState();
47-
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? account) {
48+
49+
final GoogleSignIn signIn = GoogleSignIn.instance;
50+
_signInInitialized = signIn.initialize(
51+
// Add your client IDs here as necessary for your supported platforms.
52+
);
53+
signIn.authenticationEvents.listen((GoogleSignInAuthenticationEvent event) {
54+
if (!mounted) {
55+
return;
56+
}
4857
setState(() {
49-
_currentUser = account;
58+
switch (event) {
59+
case GoogleSignInAuthenticationEventSignIn():
60+
_currentUser = event.user;
61+
case GoogleSignInAuthenticationEventSignOut():
62+
_currentUser = null;
63+
_authorization = null;
64+
}
5065
});
66+
5167
if (_currentUser != null) {
52-
_handleGetContact();
68+
_checkAuthorization();
5369
}
70+
}).onError((Object error) {
71+
debugPrint(error.toString());
72+
});
73+
74+
_signInInitialized.then((void value) {
75+
signIn.attemptLightweightAuthentication();
5476
});
55-
_googleSignIn.signInSilently();
5677
}
5778

58-
Future<void> _handleGetContact() async {
79+
void _updateAuthorization(GoogleSignInClientAuthorization? authorization) {
80+
if (!mounted) {
81+
return;
82+
}
5983
setState(() {
60-
_contactText = 'Loading contact info...';
84+
_authorization = authorization;
6185
});
6286

63-
// #docregion CreateAPIClient
64-
// Retrieve an [auth.AuthClient] from the current [GoogleSignIn] instance.
65-
final auth.AuthClient? client = await _googleSignIn.authenticatedClient();
87+
if (authorization != null) {
88+
unawaited(_handleGetContact(authorization));
89+
}
90+
}
6691

67-
assert(client != null, 'Authenticated client missing!');
92+
Future<void> _checkAuthorization() async {
93+
_updateAuthorization(
94+
await _currentUser?.authorizationClient.authorizationForScopes(scopes));
95+
}
96+
97+
Future<void> _requestAuthorization() async {
98+
_updateAuthorization(await _currentUser?.authorizationClient
99+
.authorizeScopes(<String>[PeopleServiceApi.contactsReadonlyScope]));
100+
}
101+
102+
Future<void> _handleGetContact(
103+
GoogleSignInClientAuthorization authorization) async {
104+
if (!mounted) {
105+
return;
106+
}
107+
setState(() {
108+
_contactText = 'Loading contact info...';
109+
});
110+
111+
// #docregion CreateAPIClient
112+
// Retrieve an [auth.AuthClient] from a GoogleSignInClientAuthorization.
113+
final auth.AuthClient client = authorization.authClient(scopes: scopes);
68114

69115
// Prepare a People Service authenticated client.
70-
final PeopleServiceApi peopleApi = PeopleServiceApi(client!);
71-
// Retrieve a list of the `names` of my `connections`
116+
final PeopleServiceApi peopleApi = PeopleServiceApi(client);
117+
// Retrieve a list of connected contacts' names.
72118
final ListConnectionsResponse response =
73119
await peopleApi.people.connections.list(
74120
'people/me',
@@ -79,13 +125,15 @@ class SignInDemoState extends State<SignInDemo> {
79125
final String? firstNamedContactName =
80126
_pickFirstNamedContact(response.connections);
81127

82-
setState(() {
83-
if (firstNamedContactName != null) {
84-
_contactText = 'I see you know $firstNamedContactName!';
85-
} else {
86-
_contactText = 'No contacts to display.';
87-
}
88-
});
128+
if (mounted) {
129+
setState(() {
130+
if (firstNamedContactName != null) {
131+
_contactText = 'I see you know $firstNamedContactName!';
132+
} else {
133+
_contactText = 'No contacts to display.';
134+
}
135+
});
136+
}
89137
}
90138

91139
String? _pickFirstNamedContact(List<Person>? connections) {
@@ -102,51 +150,72 @@ class SignInDemoState extends State<SignInDemo> {
102150

103151
Future<void> _handleSignIn() async {
104152
try {
105-
await _googleSignIn.signIn();
153+
await GoogleSignIn.instance.authenticate();
106154
} catch (error) {
107-
print(error); // ignore: avoid_print
155+
debugPrint(error.toString());
108156
}
109157
}
110158

111-
Future<void> _handleSignOut() => _googleSignIn.disconnect();
159+
// Call disconnect rather than signOut to more fully reset the example app.
160+
Future<void> _handleSignOut() => GoogleSignIn.instance.disconnect();
112161

113162
Widget _buildBody() {
114-
final GoogleSignInAccount? user = _currentUser;
115-
if (user != null) {
116-
return Column(
117-
mainAxisAlignment: MainAxisAlignment.spaceAround,
118-
children: <Widget>[
119-
ListTile(
120-
leading: GoogleUserCircleAvatar(
121-
identity: user,
122-
),
123-
title: Text(user.displayName ?? ''),
124-
subtitle: Text(user.email),
125-
),
126-
const Text('Signed in successfully.'),
127-
Text(_contactText),
128-
ElevatedButton(
129-
onPressed: _handleSignOut,
130-
child: const Text('SIGN OUT'),
131-
),
132-
ElevatedButton(
133-
onPressed: _handleGetContact,
134-
child: const Text('REFRESH'),
135-
),
136-
],
137-
);
138-
} else {
139-
return Column(
140-
mainAxisAlignment: MainAxisAlignment.spaceAround,
141-
children: <Widget>[
142-
const Text('You are not currently signed in.'),
143-
ElevatedButton(
144-
onPressed: _handleSignIn,
145-
child: const Text('SIGN IN'),
146-
),
147-
],
148-
);
149-
}
163+
return FutureBuilder<void>(
164+
future: _signInInitialized,
165+
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
166+
final GoogleSignInAccount? user = _currentUser;
167+
final GoogleSignInClientAuthorization? authorization = _authorization;
168+
final List<Widget> children;
169+
if (snapshot.hasError) {
170+
children = <Widget>[
171+
const Text('Error initializing sign in.'),
172+
];
173+
} else if (snapshot.connectionState == ConnectionState.done) {
174+
children = <Widget>[
175+
if (user != null) ...<Widget>[
176+
ListTile(
177+
leading: GoogleUserCircleAvatar(
178+
identity: user,
179+
),
180+
title: Text(user.displayName ?? ''),
181+
subtitle: Text(user.email),
182+
),
183+
const Text('Signed in successfully.'),
184+
if (authorization != null) ...<Widget>[
185+
Text(_contactText),
186+
ElevatedButton(
187+
onPressed: () => _handleGetContact(authorization),
188+
child: const Text('REFRESH'),
189+
),
190+
] else ...<Widget>[
191+
ElevatedButton(
192+
onPressed: _requestAuthorization,
193+
child: const Text('LOAD CONTACTS'),
194+
),
195+
],
196+
ElevatedButton(
197+
onPressed: _handleSignOut,
198+
child: const Text('SIGN OUT'),
199+
),
200+
] else ...<Widget>[
201+
const Text('You are not currently signed in.'),
202+
ElevatedButton(
203+
onPressed: _handleSignIn,
204+
child: const Text('SIGN IN'),
205+
),
206+
],
207+
];
208+
} else {
209+
children = <Widget>[
210+
const CircularProgressIndicator(),
211+
];
212+
}
213+
214+
return Column(
215+
mainAxisAlignment: MainAxisAlignment.spaceAround,
216+
children: children,
217+
);
218+
});
150219
}
151220

152221
@override

packages/extension_google_sign_in_as_googleapis_auth/example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies:
1616
path: ../
1717
flutter:
1818
sdk: flutter
19-
google_sign_in: ^6.0.0
19+
google_sign_in: ^7.0.0
2020
googleapis: ">=10.1.0 <14.0.0"
2121
googleapis_auth: ^1.1.0
2222

packages/extension_google_sign_in_as_googleapis_auth/lib/extension_google_sign_in_as_googleapis_auth.dart

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,29 @@
55
import 'package:google_sign_in/google_sign_in.dart';
66
import 'package:googleapis_auth/googleapis_auth.dart' as gapis;
77
import 'package:http/http.dart' as http;
8-
import 'package:meta/meta.dart';
98

10-
/// Extension on [GoogleSignIn] that adds an `authenticatedClient` method.
11-
///
12-
/// This method can be used to retrieve an authenticated [gapis.AuthClient]
13-
/// client that can be used with the rest of the `googleapis` libraries.
14-
extension GoogleApisGoogleSignInAuth on GoogleSignIn {
15-
/// Retrieve a `googleapis` authenticated client.
16-
Future<gapis.AuthClient?> authenticatedClient({
17-
@visibleForTesting GoogleSignInAuthentication? debugAuthentication,
18-
@visibleForTesting List<String>? debugScopes,
19-
}) async {
20-
final GoogleSignInAuthentication? auth =
21-
debugAuthentication ?? await currentUser?.authentication;
22-
final String? oauthTokenString = auth?.accessToken;
23-
if (oauthTokenString == null) {
24-
return null;
25-
}
9+
/// Extension on [GoogleSignInClientAuthorization] that adds an
10+
/// `authClient` method.
11+
extension GoogleApisGoogleSignInAuth on GoogleSignInClientAuthorization {
12+
/// Returns an authenticated [gapis.AuthClient] client that can be used with
13+
/// the rest of the `googleapis` libraries.
14+
///
15+
/// The [scopes] passed here should be the same as the scopes used to request
16+
/// the authorization. Passing scopes here that have not been authorized will
17+
/// likely result in API errors when using the client.
18+
gapis.AuthClient authClient({
19+
required List<String> scopes,
20+
}) {
2621
final gapis.AccessCredentials credentials = gapis.AccessCredentials(
2722
gapis.AccessToken(
2823
'Bearer',
29-
oauthTokenString,
30-
// TODO(kevmoo): Use the correct value once it's available from authentication
31-
// See https://github.com/flutter/flutter/issues/80905
24+
accessToken,
25+
// The underlying SDKs don't provide expiry information, so set an
26+
// arbitrary distant-future time.
3227
DateTime.now().toUtc().add(const Duration(days: 365)),
3328
),
34-
null, // We don't have a refreshToken
35-
debugScopes ?? scopes,
29+
null, // The underlying SDKs don't provide a refresh token.
30+
scopes,
3631
);
3732

3833
return gapis.authenticatedClient(http.Client(), credentials);

packages/extension_google_sign_in_as_googleapis_auth/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ name: extension_google_sign_in_as_googleapis_auth
88
description: A bridge package between google_sign_in and googleapis_auth, to create Authenticated Clients from google_sign_in user credentials.
99
repository: https://github.com/flutter/packages/tree/main/packages/extension_google_sign_in_as_googleapis_auth
1010
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+extension_google_sign_in_as_googleapis_auth%22
11-
version: 2.0.13
11+
version: 3.0.0
1212

1313
environment:
1414
sdk: ^3.6.0
@@ -17,7 +17,7 @@ environment:
1717
dependencies:
1818
flutter:
1919
sdk: flutter
20-
google_sign_in: ">=5.0.0 <7.0.0"
20+
google_sign_in: ^7.0.0
2121
googleapis_auth: '>=1.1.0 <3.0.0'
2222
http: ">=0.13.0 <2.0.0"
2323
meta: ^1.3.0

0 commit comments

Comments
 (0)