[gis_web] Adds id.renderButton JS-interop. (#3011)

* [gis_web] Adds id.renderButton API.

* Modernizes JS-interop so it's more compliant with dart2wasm.
* Updates examples, tests and docs.
* Bumps major version.

* Add the GsiButtonDataExtension class.

* Make oauth2 library more dart2wasm friendly.

* Reimplement hasGrantedA[ny|ll]Scopes in Dart.

* Fix oauth example.

* Added troubleshooting section to README.

* Add happy case tests for the oauth flow.

* Fix typo in config constructors.

* dart format

* Add some error handling to the library

* Add previously_granted_scopes field to overridable token config.

Make scopes a List of Strings in the hasGranted[Any|All]Scopes method.
This commit is contained in:
David Iglesias
2023-01-10 14:53:16 -08:00
committed by GitHub
parent b4bce7db3e
commit b6fe67e49f
15 changed files with 916 additions and 455 deletions

View File

@ -1,3 +1,11 @@
## 0.2.0
* Adds `renderButton` API to `id.dart`.
* **Breaking Change:** Makes JS-interop API more `dart2wasm`-friendly.
* Removes external getters for function types
* Introduces an external getter for the whole libraries instead.
* Updates `README.md` with the new way of `import`ing the desired libraries.
## 0.1.1
* Add optional `scope` to `OverridableTokenClientConfig` object.

View File

@ -65,9 +65,29 @@ behind a [conditional import/export](https://dart.dev/guides/libraries/create-li
Once the SDK has been loaded, it can be used by importing the correct library:
* `import 'package:google_identity_services/id.dart' as id;` for Authentication
* `import 'package:google_identity_services/oauth2.dart' as oauth2;` for
Authorization.
* `import 'package:google_identity_services/id.dart';` for Authentication.
* This will expose an `id` JSObject that binds to `google.accounts.id`.
* `import 'package:google_identity_services/oauth2.dart';` for Authorization.
* This will expose an `oauth2` JSObject that binds to `google.accounts.oauth2`.
### Troubleshooting
Watch the browser's development tools JS console while using this package.
Information about errors during initialization and use of the library will be
displayed there.
Some common issues identified so far:
#### The given origin is not allowed for the given client ID
> When you perform local tests or development, **you must add both**
> `http://localhost` and `http://localhost:<port_number>` to the
> **Authorized JavaScript origins** box.
> The [Referrer-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy)
> response header must also be set to `no-referrer-when-downgrade` when using
> http and localhost.
* Read more: [Sign In with Google for Web - Setup - Get your Google API client ID](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid#get_your_google_api_client_id).
## Browser compatibility

View File

@ -1,101 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_identity_services_web/id.dart' as id;
import 'package:google_identity_services_web/src/js_interop/dom.dart';
import 'package:integration_test/integration_test.dart';
import 'package:js/js.dart';
import 'package:js/js_util.dart';
@JS('window')
external Object get domWindow;
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() async {
// Load web/mock-gis.js in the page
await installGisMock();
});
group('prompt', () {
testWidgets('supports a moment notification callback', (_) async {
id.initialize(id.IdConfiguration(client_id: 'testing_1-2-3'));
final StreamController<id.PromptMomentNotification> controller =
StreamController<id.PromptMomentNotification>();
id.prompt(allowInterop(controller.add));
final id.PromptMomentNotification moment = await controller.stream.first;
// These defaults are set in mock-gis.js
expect(moment.getMomentType(), id.MomentType.skipped);
expect(moment.getSkippedReason(), id.MomentSkippedReason.user_cancel);
});
testWidgets('calls config callback with credential response', (_) async {
const String expected = 'should_be_a_proper_jwt_token';
setMockCredentialResponse(expected);
final StreamController<id.CredentialResponse> controller =
StreamController<id.CredentialResponse>();
id.initialize(id.IdConfiguration(
client_id: 'testing_1-2-3',
callback: allowInterop(controller.add),
));
id.prompt();
final id.CredentialResponse response = await controller.stream.first;
expect(response.credential, expected);
});
});
}
/// Installs mock-gis.js in the page.
/// Returns a future that completes when the 'load' event of the script fires.
Future<void> installGisMock() {
final Completer<void> completer = Completer<void>();
final DomHtmlScriptElement script =
document.createElement('script') as DomHtmlScriptElement;
script.src = 'mock-gis.js';
setProperty(script, 'type', 'module');
callMethod(script, 'addEventListener', <Object>[
'load',
allowInterop((_) {
completer.complete();
})
]);
document.head.appendChild(script);
return completer.future;
}
void setMockCredentialResponse([String value = 'default_value']) {
callMethod(
_getGoogleAccountsId(),
'setMockCredentialResponse',
<Object>[value, 'auto'],
);
}
Object _getGoogleAccountsId() {
return _getDeepProperty<Object>(domWindow, 'google.accounts.id');
}
// Attempts to retrieve a deeply nested property from a jsObject (or die tryin')
T _getDeepProperty<T>(Object jsObject, String deepProperty) {
final List<String> properties = deepProperty.split('.');
return properties.fold(
jsObject,
(Object jsObj, String prop) => getProperty<Object>(jsObj, prop),
) as T;
}

View File

@ -0,0 +1,57 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_identity_services_web/id.dart';
import 'package:integration_test/integration_test.dart';
import 'package:js/js.dart';
import 'utils.dart' as utils;
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() async {
// Load web/mock-gis.js in the page
await utils.installGisMock();
});
group('prompt', () {
testWidgets('supports a moment notification callback', (_) async {
id.initialize(IdConfiguration(client_id: 'testing_1-2-3'));
final StreamController<PromptMomentNotification> controller =
StreamController<PromptMomentNotification>();
id.prompt(allowInterop(controller.add));
final PromptMomentNotification moment = await controller.stream.first;
// These defaults are set in mock-gis.js
expect(moment.getMomentType(), MomentType.skipped);
expect(moment.getSkippedReason(), MomentSkippedReason.user_cancel);
});
testWidgets('calls config callback with credential response', (_) async {
const String expected = 'should_be_a_proper_jwt_token';
utils.setMockCredentialResponse(expected);
final StreamController<CredentialResponse> controller =
StreamController<CredentialResponse>();
id.initialize(IdConfiguration(
client_id: 'testing_1-2-3',
callback: allowInterop(controller.add),
));
id.prompt();
final CredentialResponse response = await controller.stream.first;
expect(response.credential, expected);
});
});
}

View File

@ -0,0 +1,124 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:google_identity_services_web/oauth2.dart';
import 'package:integration_test/integration_test.dart';
import 'package:js/js.dart';
import 'utils.dart' as utils;
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() async {
// Load web/mock-gis.js in the page
await utils.installGisMock();
});
group('initTokenClient', () {
testWidgets('returns a tokenClient', (_) async {
final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: null,
scope: 'some_scope for_tests not_real',
));
expect(client, isNotNull);
});
});
group('requestAccessToken', () {
testWidgets('passes through configuration', (_) async {
final StreamController<TokenResponse> controller =
StreamController<TokenResponse>();
final List<String> scopes = <String>['some_scope', 'another', 'more'];
final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: allowInterop(controller.add),
scope: scopes.join(' '),
));
utils.setMockTokenResponse(client, 'some-non-null-auth-token-value');
client.requestAccessToken();
final TokenResponse response = await controller.stream.first;
expect(response, isNotNull);
expect(response.error, isNull);
expect(response.scope, scopes.join(' '));
});
testWidgets('configuration can be overridden', (_) async {
final StreamController<TokenResponse> controller =
StreamController<TokenResponse>();
final List<String> scopes = <String>['some_scope', 'another', 'more'];
final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: allowInterop(controller.add),
scope: 'blank',
));
utils.setMockTokenResponse(client, 'some-non-null-auth-token-value');
client.requestAccessToken(OverridableTokenClientConfig(
scope: scopes.join(' '),
));
final TokenResponse response = await controller.stream.first;
expect(response, isNotNull);
expect(response.error, isNull);
expect(response.scope, scopes.join(' '));
});
});
group('hasGranted...Scopes', () {
// mock-gis.js returns false for scopes that start with "not-granted-".
const String notGranted = 'not-granted-scope';
testWidgets('all scopes granted', (_) async {
final List<String> scopes = <String>['some_scope', 'another', 'more'];
final TokenResponse response = await utils.fakeAuthZWithScopes(scopes);
final bool all = oauth2.hasGrantedAllScopes(response, scopes);
final bool any = oauth2.hasGrantedAnyScopes(response, scopes);
expect(all, isTrue);
expect(any, isTrue);
});
testWidgets('some scopes granted', (_) async {
final List<String> scopes = <String>['some_scope', notGranted, 'more'];
final TokenResponse response = await utils.fakeAuthZWithScopes(scopes);
final bool all = oauth2.hasGrantedAllScopes(response, scopes);
final bool any = oauth2.hasGrantedAnyScopes(response, scopes);
expect(all, isFalse, reason: 'Scope: $notGranted should not be granted!');
expect(any, isTrue);
});
testWidgets('no scopes granted', (_) async {
final List<String> scopes = <String>[notGranted, '$notGranted-2'];
final TokenResponse response = await utils.fakeAuthZWithScopes(scopes);
final bool all = oauth2.hasGrantedAllScopes(response, scopes);
final bool any = oauth2.hasGrantedAnyScopes(response, scopes);
expect(all, isFalse);
expect(any, isFalse, reason: 'No scopes were granted.');
});
});
}

View File

@ -0,0 +1,76 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:google_identity_services_web/oauth2.dart';
import 'package:google_identity_services_web/src/js_interop/dom.dart';
import 'package:js/js.dart';
import 'package:js/js_util.dart';
@JS('window')
external Object get domWindow;
/// Installs mock-gis.js in the page.
/// Returns a future that completes when the 'load' event of the script fires.
Future<void> installGisMock() {
final Completer<void> completer = Completer<void>();
final DomHtmlScriptElement script =
document.createElement('script') as DomHtmlScriptElement;
script.src = 'mock-gis.js';
setProperty(script, 'type', 'module');
callMethod(script, 'addEventListener', <Object>[
'load',
allowInterop((_) {
completer.complete();
})
]);
document.head.appendChild(script);
return completer.future;
}
/// Fakes authorization with the given scopes.
Future<TokenResponse> fakeAuthZWithScopes(List<String> scopes) {
final StreamController<TokenResponse> controller =
StreamController<TokenResponse>();
final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: allowInterop(controller.add),
scope: scopes.join(' '),
));
setMockTokenResponse(client, 'some-non-null-auth-token-value');
client.requestAccessToken();
return controller.stream.first;
}
/// Sets a mock TokenResponse value in a [client].
void setMockTokenResponse(TokenClient client, [String? authToken]) {
callMethod(
client,
'setMockTokenResponse',
<Object?>[authToken],
);
}
/// Sets a mock credential response in `google.accounts.id`.
void setMockCredentialResponse([String value = 'default_value']) {
callMethod(
_getGoogleAccountsId(),
'setMockCredentialResponse',
<Object>[value, 'auto'],
);
}
Object _getGoogleAccountsId() {
return _getDeepProperty<Object>(domWindow, 'google.accounts.id');
}
// Attempts to retrieve a deeply nested property from a jsObject (or die tryin')
T _getDeepProperty<T>(Object jsObject, String deepProperty) {
final List<String> properties = deepProperty.split('.');
return properties.fold(
jsObject,
(Object jsObj, String prop) => getProperty<Object>(jsObj, prop),
) as T;
}

View File

@ -4,7 +4,7 @@
// ignore_for_file: avoid_print
import 'package:google_identity_services_web/id.dart' as id;
import 'package:google_identity_services_web/id.dart';
// #docregion use-loader
import 'package:google_identity_services_web/loader.dart' as gis;
// #enddocregion use-loader
@ -18,9 +18,8 @@ void main() async {
// #enddocregion use-loader
id.setLogLevel('debug');
final id.IdConfiguration config = id.IdConfiguration(
final IdConfiguration config = IdConfiguration(
client_id: 'your-client_id.apps.googleusercontent.com',
ux_mode: id.UxMode.popup,
callback: allowInterop(onCredentialResponse),
);
@ -32,8 +31,8 @@ void main() async {
/// Handles the ID token returned from the One Tap prompt.
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#callback
void onCredentialResponse(id.CredentialResponse o) {
final Map<String, dynamic>? payload = jwt.JwtDecoder.tryDecode(o.credential);
void onCredentialResponse(CredentialResponse o) {
final Map<String, dynamic>? payload = jwt.JwtDecoder.tryDecode(o.credential!);
if (payload != null) {
print('Hello, ${payload["name"]}');
print(o.select_by);
@ -45,8 +44,8 @@ void onCredentialResponse(id.CredentialResponse o) {
/// Handles Prompt UI status notifications.
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.prompt
void onPromptMoment(id.PromptMomentNotification o) {
final id.MomentType type = o.getMomentType();
void onPromptMoment(PromptMomentNotification o) {
final MomentType type = o.getMomentType();
print(type.runtimeType);
print(type);
print(type.index);

View File

@ -4,16 +4,31 @@
// ignore_for_file: avoid_print
import 'package:google_identity_services_web/id.dart' as id show setLogLevel;
import 'dart:convert';
import 'package:google_identity_services_web/id.dart';
import 'package:google_identity_services_web/loader.dart' as gis;
import 'package:google_identity_services_web/oauth2.dart' as oauth2;
import 'package:google_identity_services_web/oauth2.dart';
import 'package:http/http.dart' as http;
import 'package:js/js.dart' show allowInterop;
import 'package:js/js_util.dart' show getProperty;
/// The scopes to be requested
/// People API to return my profile info...
const String MY_PROFILE =
'https://content-people.googleapis.com/v1/people/me?personFields=photos%2Cnames%2CemailAddresses';
/// People API to return all my connections.
const String MY_CONNECTIONS =
'https://people.googleapis.com/v1/people/me/connections?requestMask.includeField=person.names';
/// Basic scopes for self-id
const List<String> scopes = <String>[
'email',
'profile',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
];
/// Scopes for the people API (read contacts)
const List<String> myConnectionsScopes = <String>[
'https://www.googleapis.com/auth/contacts.readonly',
];
@ -22,58 +37,77 @@ void main() async {
id.setLogLevel('debug');
final oauth2.TokenClientConfig config = oauth2.TokenClientConfig(
final TokenClientConfig config = TokenClientConfig(
client_id: 'your-client_id.apps.googleusercontent.com',
scope: scopes.join(' '),
callback: allowInterop(onTokenResponse),
error_callback: allowInterop(onError),
);
final oauth2.OverridableTokenClientConfig overridableCfg =
oauth2.OverridableTokenClientConfig(
prompt: '',
final OverridableTokenClientConfig overridableCfg =
OverridableTokenClientConfig(
scope: (scopes + myConnectionsScopes).join(' '),
);
final oauth2.TokenClient client = oauth2.initTokenClient(config);
final TokenClient client = oauth2.initTokenClient(config);
// Disable the Popup Blocker for this to work, or move this to a Button press.
client.requestAccessToken(overridableCfg);
}
/// Triggers when there's an error with the OAuth2 popup.
///
/// We cannot use the proper type for `error` here yet, because of:
/// https://github.com/dart-lang/sdk/issues/50899
Future<void> onError(Object? error) async {
print('Error! ${getProperty(error!, "type")}');
}
/// Handles the returned (auth) token response.
/// See: https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
Future<void> onTokenResponse(oauth2.TokenResponse response) async {
if (response.error != null) {
Future<void> onTokenResponse(TokenResponse token) async {
if (token.error != null) {
print('Authorization error!');
print(response.error);
print(response.error_description);
print(response.error_uri);
return;
}
// Has granted all the scopes?
if (!oauth2.hasGrantedAllScopes(response, scopes[2])) {
print('The user has NOT granted the required scope!');
print(token.error);
print(token.error_description);
print(token.error_uri);
return;
}
// Attempt to do a request to the `people` API
final http.Response apiResponse = await http.get(
Uri.parse('https://people.googleapis.com/v1/people/me/connections'
'?requestMask.includeField=person.names'),
headers: <String, String>{
'Authorization': '${response.token_type} ${response.access_token}',
},
);
if (apiResponse.statusCode == 200) {
print('People API ${apiResponse.statusCode} OK!');
} else {
print(
'People API ${apiResponse.statusCode} Oops! Something wrong happened!');
final Object? profile = await get(token, MY_PROFILE);
print(profile);
// Has granted all the scopes?
if (!oauth2.hasGrantedAllScopes(token, myConnectionsScopes)) {
print('The user has NOT granted all the required scopes!');
print('The next get will probably throw an exception!');
}
print(apiResponse.body);
final Object? contacts = await get(token, MY_CONNECTIONS);
print(contacts);
print('Revoking token...');
oauth2.revokeToken(response.access_token, allowInterop((String status) {
print(status);
oauth2.revoke(token.access_token,
allowInterop((TokenRevocationResponse response) {
print(response.successful);
print(response.error);
print(response.error_description);
}));
}
/// Gets from [url] with an authorization header defined by [token].
///
/// Attempts to [jsonDecode] the result.
Future<Object?> get(TokenResponse token, String url) async {
final Uri uri = Uri.parse(url);
final http.Response response = await http.get(uri, headers: <String, String>{
'Authorization': '${token.token_type} ${token.access_token}',
});
if (response.statusCode != 200) {
throw http.ClientException(response.body, uri);
}
return jsonDecode(response.body) as Object?;
}

View File

@ -94,17 +94,24 @@ class TokenClient {
this.config = config;
}
requestAccessToken(overridableConfig) {
this.overridableConfig = overridableConfig;
let callback = this.overridableConfig.callback || this.config.callback;
this.config = {...this.config, ...overridableConfig};
let callback = this.config.callback;
if (!callback) {
return;
}
callAsync(() => {
callback(this.tokenResponse);
callback({
...this.tokenResponse,
scope: this.config.scope,
});
});
}
setMockTokenResponse(tokenResponse) {
this.tokenResponse = tokenResponse;
setMockTokenResponse(access_token) {
this.tokenResponse = {
access_token: access_token,
token_type: access_token != null ? 'Bearer' : null,
error: access_token == null ? 'unauthorized' : null,
};
}
}
@ -116,22 +123,24 @@ class Oauth2 {
return new TokenClient(config);
}
hasGrantedAllScopes(tokenResponse, scope, ...scopes) {
return tokenResponse != null;
return tokenResponse != null && !scope.startsWith('not-granted-');
}
hasGrantedAnyScopes(tokenResponse, scope, ...scopes) {
return tokenResponse != null;
return false; // Unused in the lib
}
revoke(accessToken, done) {
if (!done) {
return;
}
callAsync(() => {
done();
done({
success: true,
});
})
}
}
function mockGis() {
(function() {
let goog = {
accounts: {
id: new Id(),
@ -139,6 +148,4 @@ function mockGis() {
}
};
globalThis['google'] = goog;
}
mockGis();
}());

View File

@ -88,6 +88,29 @@ extension DomHtmlScriptElementExtension on DomHtmlScriptElement {
external set defer(bool defer);
}
/// Error object
@JS('Error')
@staticInterop
abstract class DomError {}
/// Methods on the error object
extension DomErrorExtension on DomError {
/// Error message.
external String? get message;
/// Stack trace.
external String? get stack;
/// Error name. This is determined by the constructor function.
external String get name;
/// Error cause indicating the reason why the current error is thrown.
///
/// This is usually another caught error, or the value provided as the `cause`
/// property of the Error constructor's second argument.
external Object? get cause;
}
/*
// Trusted Types API (TrustedTypePolicy, TrustedScript, TrustedScriptURL)
// https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypesAPI

View File

@ -9,54 +9,142 @@
// * non_constant_identifier_names required to be able to use the same parameter
// names as the underlying library.
@JS('google.accounts.id')
library id;
@JS()
library google_accounts_id;
import 'package:js/js.dart';
import 'dom.dart';
import 'shared.dart';
/// An undocumented method. Try with 'debug'.
/// Binding to the `google.accounts.id` JS global.
///
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference
@JS('google.accounts.id')
external GoogleAccountsId get id;
/// The Dart definition of the `google.accounts.id` global.
@JS()
external SetLogLevelFn get setLogLevel;
@staticInterop
abstract class GoogleAccountsId {}
///
typedef SetLogLevelFn = void Function(String level);
/// The `google.accounts.id` methods
extension GoogleAccountsIdExtension on GoogleAccountsId {
/// An undocumented method.
///
/// Try it with 'debug'.
external void setLogLevel(String level);
/*
// Method: google.accounts.id.initialize
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.initialize
*/
/// Initializes the Sign In With Google client based on [IdConfiguration].
///
/// The `initialize` method creates a Sign In With Google client instance that
/// can be implicitly used by all modules in the same web page.
///
/// * You only need to call the `initialize` method once even if you use
/// multiple modules (like One Tap, Personalized button, revocation, etc.) in
/// the same web page.
/// * If you do call the google.accounts.id.initialize method multiple times,
/// only the configurations in the last call will be remembered and used.
///
/// You actually reset the configurations whenever you call the `initialize`
/// method, and all subsequent methods in the same web page will use the new
/// configurations immediately.
///
/// WARNING: The `initialize` method should be called only once, even if you
/// use both One Tap and button in the same web page.
///
/// Method: google.accounts.id.initialize
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.initialize
external void initialize(IdConfiguration idConfiguration);
/// Initializes the Sign In With Google client based on [IdConfiguration].
///
/// The `initialize` method creates a Sign In With Google client instance that
/// can be implicitly used by all modules in the same web page.
///
/// * You only need to call the `initialize` method once even if you use
/// multiple modules (like One Tap, Personalized button, revocation, etc.) in
/// the same web page.
/// * If you do call the google.accounts.id.initialize method multiple times,
/// only the configurations in the last call will be remembered and used.
///
/// You actually reset the configurations whenever you call the `initialize`
/// method, and all subsequent methods in the same web page will use the new
/// configurations immediately.
///
/// WARNING: The `initialize` method should be called only once, even if you
/// use both One Tap and button in the same web page.
@JS()
external InitializeFn get initialize;
/// The `prompt` method displays the One Tap prompt or the browser native
/// credential manager after the [initialize] method is invoked.
///
/// Normally, the `prompt` method is called on page load. Due to the session
/// status and user settings on the Google side, the One Tap prompt UI might
/// not be displayed. To get notified on the UI status for different moments,
/// pass a [PromptMomentListenerFn] to receive UI status notifications.
///
/// Notifications are fired on the following moments:
///
/// * Display moment: This occurs after the `prompt` method is called. The
/// notification contains a boolean value to indicate whether the UI is
/// displayed or not.
/// * Skipped moment: This occurs when the One Tap prompt is closed by an auto
/// cancel, a manual cancel, or when Google fails to issue a credential, such
/// as when the selected session has signed out of Google.
/// In these cases, we recommend that you continue on to the next identity
/// providers, if there are any.
/// * Dismissed moment: This occurs when Google successfully retrieves a
/// credential or a user wants to stop the credential retrieval flow. For
/// example, when the user begins to input their username and password in
/// your login dialog, you can call the [cancel] method to close the One Tap
/// prompt and trigger a dismissed moment.
///
/// WARNING: When on a dismissed moment, do not try any of the next identity
/// providers.
///
/// Method: google.accounts.id.prompt
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.prompt
external void prompt([PromptMomentListenerFn momentListener]);
/// The type of the [initialize] function.
typedef InitializeFn = void Function(IdConfiguration idConfiguration);
/// Renders a Sign In With Google button in your web page.
///
/// Method: google.accounts.id.renderButton
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.renderButton
external void renderButton(
DomHtmlElement parent, [
GsiButtonConfiguration options,
]);
/*
// Data type: IdConfiguration
// https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration
*/
/// Record when the user signs out of your website in cookies.
///
/// This prevents a UX dead loop.
///
/// Method: google.accounts.id.disableAutoselect
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.disableAutoSelect
external void disableAutoSelect();
/// A wrapper for the `store` method of the browser's native credential manager API.
///
/// It can only be used to store a Password [Credential].
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/store
///
/// Method: google.accounts.id.storeCredential
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.storeCredential
external void storeCredential(Credential credential, [VoidFn fallback]);
/// Cancels the One Tap flow.
///
/// You can cancel the One Tap flow if you remove the prompt from the relying
/// party DOM. The cancel operation is ignored if a credential is already
/// selected.
///
/// Method: google.accounts.id.cancel
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.cancel
external void cancel();
/// Revokes the OAuth grant used to share the ID token for the specified user.
///
/// [hint] is the email address or unique ID of the user's Google Account. The
/// ID is the `sub` property of the [CredentialResponse.credential] payload.
///
/// The optional [callback] is a function that gets called to report on the
/// success of the revocation call.
///
/// The [callback] parameter must be manually wrapped in [allowInterop]
/// before being passed to the [revoke] function.
///
/// Method: google.accounts.id.revoke
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.revoke
external void revoke(String hint, [RevocationResponseHandlerFn callback]);
}
/// The configuration object for the [initialize] method.
///
/// Data type: IdConfiguration
/// https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration
@JS()
@anonymous
@staticInterop
@ -152,55 +240,13 @@ abstract class IdConfiguration {
});
}
/*
// Method: google.accounts.id.prompt
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.prompt
*/
/// The `prompt` method displays the One Tap prompt or the browser native
/// credential manager after the [initialize] method is invoked.
///
/// Normally, the `prompt` method is called on page load. Due to the session
/// status and user settings on the Google side, the One Tap prompt UI might
/// not be displayed. To get notified on the UI status for different moments,
/// pass a [PromptMomentListenerFn] to receive UI status notifications.
///
/// Notifications are fired on the following moments:
///
/// * Display moment: This occurs after the `prompt` method is called. The
/// notification contains a boolean value to indicate whether the UI is
/// displayed or not.
/// * Skipped moment: This occurs when the One Tap prompt is closed by an auto
/// cancel, a manual cancel, or when Google fails to issue a credential, such
/// as when the selected session has signed out of Google.
/// In these cases, we recommend that you continue on to the next identity
/// providers, if there are any.
/// * Dismissed moment: This occurs when Google successfully retrieves a
/// credential or a user wants to stop the credential retrieval flow. For
/// example, when the user begins to input their username and password in
/// your login dialog, you can call the [cancel] method to close the One Tap
/// prompt and trigger a dismissed moment.
///
/// WARNING: When on a dismissed moment, do not try any of the next identity
/// providers.
@JS()
external PromptFn get prompt;
/// The type of the [prompt] function.
///
/// The [momentListener] parameter must be manually wrapped in [allowInterop]
/// before being passed to the [prompt] function.
typedef PromptFn = void Function([PromptMomentListenerFn momentListener]);
/// The type of the function that can be passed to [prompt] to listen for [PromptMomentNotification]s.
typedef PromptMomentListenerFn = void Function(PromptMomentNotification moment);
/*
// Data type: PromptMomentNotification
// https://developers.google.com/identity/gsi/web/reference/js-reference#PromptMomentNotification
*/
/// A moment (status) notification from the [prompt] method.
///
/// Data type: PromptMomentNotification
/// https://developers.google.com/identity/gsi/web/reference/js-reference#PromptMomentNotification
@JS()
@staticInterop
abstract class PromptMomentNotification {}
@ -246,25 +292,32 @@ extension PromptMomentNotificationExtension on PromptMomentNotification {
maybeEnum(_getDismissedReason(), MomentDismissedReason.values);
}
/*
// Data type: CredentialResponse
// https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse
*/
/// The object passed as the parameter of your [CallbackFn].
///
/// Data type: CredentialResponse
/// https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse
@JS()
@staticInterop
abstract class CredentialResponse {}
/// The fields that are contained in the credential response object.
extension CredentialResponseExtension on CredentialResponse {
/// The ClientID for this Credential.
external String? get client_id;
/// Error while signing in.
external String? get error;
/// Details of the error while signing in.
external String? get error_detail;
/// This field is the ID token as a base64-encoded JSON Web Token (JWT)
/// string.
///
/// See more: https://developers.google.com/identity/gsi/web/reference/js-reference#credential
external String get credential;
external String? get credential;
@JS('select_by')
external String get _select_by;
external String? get _select_by;
/// This field sets how the credential was selected.
///
@ -272,8 +325,8 @@ extension CredentialResponseExtension on CredentialResponse {
/// to set the value.
///
/// See more: https://developers.google.com/identity/gsi/web/reference/js-reference#select_by
CredentialSelectBy get select_by =>
CredentialSelectBy.values.byName(_select_by);
CredentialSelectBy? get select_by =>
maybeEnum(_select_by, CredentialSelectBy.values);
}
/// The type of the `callback` used to create an [IdConfiguration].
@ -285,20 +338,69 @@ extension CredentialResponseExtension on CredentialResponse {
/// attribute.
typedef CallbackFn = void Function(CredentialResponse credentialResponse);
/*
// Method: google.accounts.id.renderButton
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.renderButton
//
// Data type: GsiButtonConfiguration
// https://developers.google.com/identity/gsi/web/reference/js-reference#GsiButtonConfiguration
//
// Question: Do we need to implement renderButton and its options?
*/
/// The configuration object for the [renderButton] method.
///
/// Data type: GsiButtonConfiguration
/// https://developers.google.com/identity/gsi/web/reference/js-reference#GsiButtonConfiguration
@JS()
@anonymous
@staticInterop
abstract class GsiButtonConfiguration {
/// Constructs an options object for the [renderButton] method.
///
/// The following properties need to be manually wrapped in [allowInterop]
/// before being passed to this constructor:
external factory GsiButtonConfiguration({
/// The button type.
ButtonType type,
/*
// Data type: Credential
// https://developers.google.com/identity/gsi/web/reference/js-reference#type-Credential
*/
/// The button theme.
ButtonTheme theme,
/// The button size.
ButtonSize size,
/// The button text.
ButtonText text,
/// The button shape.
ButtonShape shape,
/// The Google logo alignment in the button.
ButtonLogoAlignment logo_alignment,
/// The minimum button width, in pixels.
///
/// The maximum width is 400 pixels.
double width,
/// The pre-set locale of the button text.
///
/// If not set, the browser's default locale or the Google session user's
/// preference is used.
String locale,
/// A function to be called when the button is clicked.
GsiButtonClickListenerFn click_listener,
});
}
/// The object passed as an optional parameter to `click_listener` function.
@JS()
@staticInterop
abstract class GsiButtonData {}
/// The fields that are contained in the button data.
extension GsiButtonDataExtension on GsiButtonData {
/// Nonce
external String? get nonce;
/// State
external String? get state;
}
/// The type of the [GsiButtonConfiguration] `click_listener` function.
typedef GsiButtonClickListenerFn = void Function(GsiButtonData? gsiButtonData);
/// The object passed to the [NativeCallbackFn]. Represents a PasswordCredential
/// that was returned by the Browser.
@ -307,6 +409,9 @@ typedef CallbackFn = void Function(CredentialResponse credentialResponse);
/// in the browser through the [storeCredential] method.
///
/// See also: https://developer.mozilla.org/en-US/docs/Web/API/PasswordCredential/PasswordCredential
///
/// Data type: Credential
/// https://developers.google.com/identity/gsi/web/reference/js-reference#type-Credential
@JS()
@anonymous
@staticInterop
@ -333,95 +438,22 @@ extension CredentialExtension on Credential {
/// from the native Credential manager of the user's browser.
typedef NativeCallbackFn = void Function(Credential credential);
/*
// Method: google.accounts.id.disableAutoselect
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.disableAutoSelect
*/
/// When the user signs out of your website, you need to call this method to
/// record the status in cookies.
///
/// This prevents a UX dead loop.
@JS()
external VoidFn get disableAutoSelect;
/*
// Method: google.accounts.id.storeCredential
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.storeCredential
*/
/// This method is a simple wrapper for the `store` method of the browser's
/// native credential manager API.
///
/// It can only be used to store a Password [Credential].
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/store
@JS()
external StoreCredentialFn get storeCredential;
/// The type of the [storeCredential] function.
///
/// The [callback] parameter must be manually wrapped in [allowInterop]
/// before being passed to the [storeCredential] function.
// Question: What's the type of the callback function??? VoidFn?
typedef StoreCredentialFn = void Function(
Credential credential,
Function? callback,
);
/*
// Method: google.accounts.id.cancel
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.cancel
*/
/// You can cancel the One Tap flow if you remove the prompt from the relying
/// party DOM. The cancel operation is ignored if a credential is already
/// selected.
@JS()
external VoidFn get cancel;
/*
// Library load callback: onGoogleLibraryLoad
// https://developers.google.com/identity/gsi/web/reference/js-reference#onGoogleLibraryLoad
// See: `load_callback.dart` and `loader.dart`
*/
/*
// Method: google.accounts.id.revoke
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.revoke
*/
/// The `revoke` method revokes the OAuth grant used to share the ID token for
/// the specified user.
@JS()
external RevokeFn get revoke;
/// The type of the [revoke] function.
///
/// [hint] is the email address or unique ID of the user's Google Account. The
/// ID is the `sub` property of the [CredentialResponse.credential] payload.
///
/// The optional [callback] is a function that gets called to report on the
/// success of the revocation call.
///
/// The [callback] parameter must be manually wrapped in [allowInterop]
/// before being passed to the [revoke] function.
typedef RevokeFn = void Function(String hint,
[RevocationResponseHandlerFn callback]);
/// The type of the `callback` function passed to [revoke], to be notified of
/// the success of the revocation operation.
typedef RevocationResponseHandlerFn = void Function(
RevocationResponse revocationResponse,
);
/*
// Data type: RevocationResponse
// https://developers.google.com/identity/gsi/web/reference/js-reference#RevocationResponse
*/
/// The parameter passed to the optional [RevocationResponseHandlerFn]
/// `callback` of the [revoke] function.
/// The parameter passed to the `callback` of the [revoke] function.
///
/// Data type: RevocationResponse
/// https://developers.google.com/identity/gsi/web/reference/js-reference#RevocationResponse
@JS()
@staticInterop
abstract class RevocationResponse {}

View File

@ -9,34 +9,84 @@
// * non_constant_identifier_names required to be able to use the same parameter
// names as the underlying library.
@JS('google.accounts.oauth2')
library oauth2;
@JS()
library google_accounts_oauth2;
import 'package:js/js.dart';
import 'dom.dart';
import 'shared.dart';
// Code Client
/// Binding to the `google.accounts.oauth2` JS global.
///
/// See: https://developers.google.com/identity/oauth2/web/reference/js-reference
@JS('google.accounts.oauth2')
external GoogleAccountsOauth2 get oauth2;
/*
// Method: google.accounts.oauth2.initCodeClient
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.initCodeClient
*/
/// The initCodeClient method initializes and returns a code client, with the
/// passed-in [config].
/// The Dart definition of the `google.accounts.oauth2` global.
@JS()
external InitCodeClientFn get initCodeClient;
@staticInterop
abstract class GoogleAccountsOauth2 {}
/// The type of the [initCodeClient] function.
typedef InitCodeClientFn = CodeClient Function(CodeClientConfig config);
/// The `google.accounts.oauth2` methods
extension GoogleAccountsOauth2Extension on GoogleAccountsOauth2 {
/// Initializes and returns a code client, with the passed-in [config].
///
/// Method: google.accounts.oauth2.initCodeClient
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.initCodeClient
external CodeClient initCodeClient(CodeClientConfig config);
/*
// Data type: CodeClientConfig
// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClientConfig
*/
/// Initializes and returns a token client, with the passed-in [config].
///
/// Method: google.accounts.oauth2.initTokenClient
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.initTokenClient
external TokenClient initTokenClient(TokenClientConfig config);
// Method: google.accounts.oauth2.hasGrantedAllScopes
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAllScopes
@JS('hasGrantedAllScopes')
external bool _hasGrantedScope(TokenResponse token, String scope);
/// Checks if hte user has granted **all** the specified [scopes].
///
/// [scopes] is a space-separated list of scope names.
///
/// Method: google.accounts.oauth2.hasGrantedAllScopes
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAllScopes
bool hasGrantedAllScopes(TokenResponse tokenResponse, List<String> scopes) {
return scopes
.every((String scope) => _hasGrantedScope(tokenResponse, scope));
}
/// Checks if hte user has granted **all** the specified [scopes].
///
/// [scopes] is a space-separated list of scope names.
///
/// Method: google.accounts.oauth2.hasGrantedAllScopes
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAllScopes
bool hasGrantedAnyScopes(TokenResponse tokenResponse, List<String> scopes) {
return scopes.any((String scope) => _hasGrantedScope(tokenResponse, scope));
}
/// Revokes all of the scopes that the user granted to the app.
///
/// A valid [accessToken] is required to revoke permissions.
///
/// The [done] callback is called once the revoke action is done. It must be
/// manually wrapped in [allowInterop] before being passed to this method.
///
/// Method: google.accounts.oauth2.revoke
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.revoke
external void revoke(
String accessToken, [
RevokeTokenDoneFn done,
]);
}
/// The configuration object for the [initCodeClient] method.
///
/// Data type: CodeClientConfig
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClientConfig
@JS()
@anonymous
@staticInterop
@ -51,6 +101,7 @@ abstract class CodeClientConfig {
String? redirect_uri,
bool? auto_select,
CodeClientCallbackFn? callback,
ErrorCallbackFn? error_callback,
String? state,
bool? enable_serial_consent,
String? hint,
@ -60,14 +111,12 @@ abstract class CodeClientConfig {
});
}
/*
// Data type: CodeClient
// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClient
*/
/// A client that can start the OAuth 2.0 Code UX flow.
///
/// See: https://developers.google.com/identity/oauth2/web/guides/use-code-model
///
/// Data type: CodeClient
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClient
@JS()
@staticInterop
abstract class CodeClient {}
@ -78,12 +127,10 @@ extension CodeClientExtension on CodeClient {
external void requestCode();
}
/*
// Data type: CodeResponse
// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeResponse
*/
/// The object passed as the parameter of your [CodeClientCallbackFn].
///
/// Data type: CodeResponse
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeResponse
@JS()
@staticInterop
abstract class CodeResponse {}
@ -116,27 +163,10 @@ extension CodeResponseExtension on CodeResponse {
/// The type of the `callback` function passed to [CodeClientConfig].
typedef CodeClientCallbackFn = void Function(CodeResponse response);
// Token Client
/*
// Method: google.accounts.oauth2.initTokenClient
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.initTokenClient
*/
/// The initCodeClient method initializes and returns a code client, with the
/// passed-in [config].
@JS()
external InitTokenClientFn get initTokenClient;
/// The type of the [initCodeClient] function.
typedef InitTokenClientFn = TokenClient Function(TokenClientConfig config);
/*
// Data type: TokenClientConfig
// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig
*/
/// The configuration object for the [initTokenClient] method.
///
/// Data type: TokenClientConfig
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig
@JS()
@anonymous
@staticInterop
@ -149,6 +179,7 @@ abstract class TokenClientConfig {
required String client_id,
required TokenClientCallbackFn? callback,
required String scope,
ErrorCallbackFn? error_callback,
String? prompt,
bool? enable_serial_consent,
String? hint,
@ -157,14 +188,12 @@ abstract class TokenClientConfig {
});
}
/*
// Data type: TokenClient
// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClient
*/
/// A client that can start the OAuth 2.0 Token UX flow.
///
/// See: https://developers.google.com/identity/oauth2/web/guides/use-token-model
///
/// Data type: TokenClient
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClient
@JS()
@staticInterop
abstract class TokenClient {}
@ -177,13 +206,10 @@ extension TokenClientExtension on TokenClient {
]);
}
/*
// Data type: OverridableTokenClientConfig
// https://developers.google.com/identity/oauth2/web/reference/js-reference#OverridableTokenClientConfig
*/
/// The overridable configuration object for the
/// [TokenClientExtension.requestAccessToken] method.
/// The overridable configuration object for the [TokenClientExtension.requestAccessToken] method.
///
/// Data type: OverridableTokenClientConfig
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#OverridableTokenClientConfig
@JS()
@anonymous
@staticInterop
@ -229,15 +255,18 @@ abstract class OverridableTokenClientConfig {
/// uses to maintain state between your authorization request and the
/// authorization server's response.
String? state,
/// Preserves previously requested scopes in this new request.
///
/// (Undocumented)
bool? include_granted_scopes,
});
}
/*
// Data type: TokenResponse
// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
*/
/// The object passed as the parameter of your [TokenClientCallbackFn].
///
/// Data type: TokenResponse
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
@JS()
@staticInterop
abstract class TokenResponse {}
@ -283,63 +312,55 @@ extension TokenResponseExtension on TokenResponse {
/// The type of the `callback` function passed to [TokenClientConfig].
typedef TokenClientCallbackFn = void Function(TokenResponse response);
/*
// Method: google.accounts.oauth2.hasGrantedAllScopes
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAllScopes
*/
/// The type of the `error_callback` in both oauth2 initXClient calls.
///
/// (Currently undocumented)
///
/// `error` should be of type [GoogleIdentityServicesError]?, but it cannot be
/// because of this DDC bug: https://github.com/dart-lang/sdk/issues/50899
typedef ErrorCallbackFn = void Function(Object? error);
/// Checks if the user granted **all** the specified scopes.
/// An error returned by `initTokenClient` or `initDataClient`.
///
/// Cannot be used: https://github.com/dart-lang/sdk/issues/50899
@JS()
external HasGrantedScopesFn get hasGrantedAllScopes;
@staticInterop
abstract class GoogleIdentityServicesError extends DomError {}
/*
// Method: google.accounts.oauth2.hasGrantedAnyScopes
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAnyScopes
*/
/// Methods of the GoogleIdentityServicesError object.
///
/// Cannot be used: https://github.com/dart-lang/sdk/issues/50899
extension GoogleIdentityServicesErrorExtension on GoogleIdentityServicesError {
@JS('type')
external String get _type;
// String get _type => js_util.getProperty<String>(this, 'type');
/// Checks if the user granted **any** of the specified scopes.
/// The type of error
GoogleIdentityServicesErrorType get type =>
GoogleIdentityServicesErrorType.values.byName(_type);
}
/// The signature of the `done` function for [revoke].
typedef RevokeTokenDoneFn = void Function(TokenRevocationResponse response);
/// The parameter passed to the `callback` of the [revoke] function.
///
/// Data type: RevocationResponse
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
@JS()
external HasGrantedScopesFn get hasGrantedAnyScopes;
@staticInterop
abstract class TokenRevocationResponse {}
/// The signature for functions that check if any/all scopes have been granted.
///
/// Used by [hasGrantedAllScopes] and [hasGrantedAnyScope].
typedef HasGrantedScopesFn = bool Function(
TokenResponse tokenResponse,
String firstScope, [
String? scope2,
String? scope3,
String? scope4,
String? scope5,
String? scope6,
String? scope7,
String? scope8,
String? scope9,
String? scope10,
]);
/// The fields that are contained in the [TokenRevocationResponse] object.
extension TokenRevocationResponseExtension on TokenRevocationResponse {
/// This field is a boolean value set to true if the revoke method call
/// succeeded or false on failure.
external bool get successful;
/*
// Method: google.accounts.oauth2.revoke
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.revoke
*/
/// This field is a string value and contains a detailed error message if the
/// revoke method call failed, it is undefined on success.
external String? get error;
/// The [revokeToken] method revokes all of the scopes that the user granted to
/// the app. A valid access token is required to revoke the permission.
///
/// The `done` callback is called once the revoke action is done.
@JS('revoke')
external RevokeTokenFn get revokeToken;
/// The signature of the [revokeToken] function.
///
/// The (optional) [done] parameter must be manually wrapped in [allowInterop]
/// before being passed to the [revokeToken] function.
typedef RevokeTokenFn = void Function(
String accessToken, [
RevokeTokenDoneFn done,
]);
/// The signature of the `done` function for [revokeToken].
///
/// Work in progress here: b/248628502
typedef RevokeTokenDoneFn = void Function(String jsonError);
/// The description of the error.
external String? get error_description;
}

View File

@ -213,3 +213,159 @@ enum CredentialSelectBy {
@override
String toString() => _selectBy;
}
/// The type of button to be rendered.
///
/// https://developers.google.com/identity/gsi/web/reference/js-reference#type
enum ButtonType {
/// A button with text or personalized information.
standard('standard'),
/// An icon button without text.
icon('icon');
///
const ButtonType(String type) : _type = type;
final String _type;
@override
String toString() => _type;
}
/// The theme of the button to be rendered.
///
/// https://developers.google.com/identity/gsi/web/reference/js-reference#theme
enum ButtonTheme {
/// A standard button theme.
outline('outline'),
/// A blue-filled button theme.
filled_blue('filled_blue'),
/// A black-filled button theme.
filled_black('filled_black');
///
const ButtonTheme(String theme) : _theme = theme;
final String _theme;
@override
String toString() => _theme;
}
/// The theme of the button to be rendered.
///
/// https://developers.google.com/identity/gsi/web/reference/js-reference#size
enum ButtonSize {
/// A large button (about 40px tall).
large('large'),
/// A medium-sized button (about 32px tall).
medium('medium'),
/// A small button (about 20px tall).
small('small');
///
const ButtonSize(String size) : _size = size;
final String _size;
@override
String toString() => _size;
}
/// The button text.
///
/// https://developers.google.com/identity/gsi/web/reference/js-reference#text
enum ButtonText {
/// The button text is "Sign in with Google".
signin_with('signin_with'),
/// The button text is "Sign up with Google".
signup_with('signup_with'),
/// The button text is "Continue with Google".
continue_with('continue_with'),
/// The button text is "Sign in".
signin('signin');
///
const ButtonText(String text) : _text = text;
final String _text;
@override
String toString() => _text;
}
/// The button shape.
///
/// https://developers.google.com/identity/gsi/web/reference/js-reference#shape
enum ButtonShape {
/// The rectangular-shaped button.
///
/// If used for the [ButtonType.icon], then it's the same as [square].
rectangular('rectangular'),
/// The pill-shaped button.
///
/// If used for the [ButtonType.icon], then it's the same as [circle].
pill('pill'),
/// The circle-shaped button.
///
/// If used for the [ButtonType.standard], then it's the same as [pill].
circle('circle'),
/// The square-shaped button.
///
/// If used for the [ButtonType.standard], then it's the same as [rectangular].
square('square');
///
const ButtonShape(String shape) : _shape = shape;
final String _shape;
@override
String toString() => _shape;
}
/// The type of button to be rendered.
///
/// https://developers.google.com/identity/gsi/web/reference/js-reference#type
enum ButtonLogoAlignment {
/// Left-aligns the Google logo.
left('left'),
/// Center-aligns the Google logo.
center('center');
///
const ButtonLogoAlignment(String alignment) : _alignment = alignment;
final String _alignment;
@override
String toString() => _alignment;
}
/// The `type` of the error object passed into the `error_callback` function.
enum GoogleIdentityServicesErrorType {
/// Missing required parameter.
missing_required_parameter('missing_required_parameter'),
/// The popup was closed before the flow was completed.
popup_closed('popup_closed'),
/// Popup failed to open.
popup_failed_to_open('popup_failed_to_open'),
/// Unknown error.
unknown('unknown');
///
const GoogleIdentityServicesErrorType(String type) : _type = type;
final String _type;
@override
String toString() => _type;
}

View File

@ -2,7 +2,7 @@ name: google_identity_services_web
description: A Dart JS-interop layer for Google Identity Services. Google's new sign-in SDK for Web that supports multiple types of credentials.
repository: https://github.com/flutter/packages/tree/main/packages/google_identity_services_web
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_identiy_services_web%22
version: 0.1.1
version: 0.2.0
environment:
sdk: ">=2.17.0 <3.0.0"

View File

@ -1,3 +1,8 @@
# Tests
Use `dart run tool/run_tests.dart` to run tests in this package.
## Failed to run Chrome: No such file or directory
Ensure the correct path to the Chrome executable is set in `dart_test.yaml`. It
may be other than `chrome` (for example, `google-chrome` in my machine).