mirror of
https://github.com/flutter/packages.git
synced 2025-07-03 09:08:54 +08:00
[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:
@ -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
|
## 0.1.1
|
||||||
|
|
||||||
* Add optional `scope` to `OverridableTokenClientConfig` object.
|
* Add optional `scope` to `OverridableTokenClientConfig` object.
|
||||||
|
@ -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:
|
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/id.dart';` for Authentication.
|
||||||
* `import 'package:google_identity_services/oauth2.dart' as oauth2;` for
|
* This will expose an `id` JSObject that binds to `google.accounts.id`.
|
||||||
Authorization.
|
* `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
|
## Browser compatibility
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -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.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// ignore_for_file: avoid_print
|
// 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
|
// #docregion use-loader
|
||||||
import 'package:google_identity_services_web/loader.dart' as gis;
|
import 'package:google_identity_services_web/loader.dart' as gis;
|
||||||
// #enddocregion use-loader
|
// #enddocregion use-loader
|
||||||
@ -18,9 +18,8 @@ void main() async {
|
|||||||
// #enddocregion use-loader
|
// #enddocregion use-loader
|
||||||
id.setLogLevel('debug');
|
id.setLogLevel('debug');
|
||||||
|
|
||||||
final id.IdConfiguration config = id.IdConfiguration(
|
final IdConfiguration config = IdConfiguration(
|
||||||
client_id: 'your-client_id.apps.googleusercontent.com',
|
client_id: 'your-client_id.apps.googleusercontent.com',
|
||||||
ux_mode: id.UxMode.popup,
|
|
||||||
callback: allowInterop(onCredentialResponse),
|
callback: allowInterop(onCredentialResponse),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -32,8 +31,8 @@ void main() async {
|
|||||||
|
|
||||||
/// Handles the ID token returned from the One Tap prompt.
|
/// Handles the ID token returned from the One Tap prompt.
|
||||||
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#callback
|
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#callback
|
||||||
void onCredentialResponse(id.CredentialResponse o) {
|
void onCredentialResponse(CredentialResponse o) {
|
||||||
final Map<String, dynamic>? payload = jwt.JwtDecoder.tryDecode(o.credential);
|
final Map<String, dynamic>? payload = jwt.JwtDecoder.tryDecode(o.credential!);
|
||||||
if (payload != null) {
|
if (payload != null) {
|
||||||
print('Hello, ${payload["name"]}');
|
print('Hello, ${payload["name"]}');
|
||||||
print(o.select_by);
|
print(o.select_by);
|
||||||
@ -45,8 +44,8 @@ void onCredentialResponse(id.CredentialResponse o) {
|
|||||||
|
|
||||||
/// Handles Prompt UI status notifications.
|
/// Handles Prompt UI status notifications.
|
||||||
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.prompt
|
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.prompt
|
||||||
void onPromptMoment(id.PromptMomentNotification o) {
|
void onPromptMoment(PromptMomentNotification o) {
|
||||||
final id.MomentType type = o.getMomentType();
|
final MomentType type = o.getMomentType();
|
||||||
print(type.runtimeType);
|
print(type.runtimeType);
|
||||||
print(type);
|
print(type);
|
||||||
print(type.index);
|
print(type.index);
|
||||||
|
@ -4,16 +4,31 @@
|
|||||||
|
|
||||||
// ignore_for_file: avoid_print
|
// 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/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:http/http.dart' as http;
|
||||||
import 'package:js/js.dart' show allowInterop;
|
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>[
|
const List<String> scopes = <String>[
|
||||||
'email',
|
'https://www.googleapis.com/auth/userinfo.profile',
|
||||||
'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',
|
'https://www.googleapis.com/auth/contacts.readonly',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -22,58 +37,77 @@ void main() async {
|
|||||||
|
|
||||||
id.setLogLevel('debug');
|
id.setLogLevel('debug');
|
||||||
|
|
||||||
final oauth2.TokenClientConfig config = oauth2.TokenClientConfig(
|
final TokenClientConfig config = TokenClientConfig(
|
||||||
client_id: 'your-client_id.apps.googleusercontent.com',
|
client_id: 'your-client_id.apps.googleusercontent.com',
|
||||||
scope: scopes.join(' '),
|
scope: scopes.join(' '),
|
||||||
callback: allowInterop(onTokenResponse),
|
callback: allowInterop(onTokenResponse),
|
||||||
|
error_callback: allowInterop(onError),
|
||||||
);
|
);
|
||||||
|
|
||||||
final oauth2.OverridableTokenClientConfig overridableCfg =
|
final OverridableTokenClientConfig overridableCfg =
|
||||||
oauth2.OverridableTokenClientConfig(
|
OverridableTokenClientConfig(
|
||||||
prompt: '',
|
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.
|
// Disable the Popup Blocker for this to work, or move this to a Button press.
|
||||||
client.requestAccessToken(overridableCfg);
|
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.
|
/// Handles the returned (auth) token response.
|
||||||
/// See: https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
|
/// See: https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
|
||||||
Future<void> onTokenResponse(oauth2.TokenResponse response) async {
|
Future<void> onTokenResponse(TokenResponse token) async {
|
||||||
if (response.error != null) {
|
if (token.error != null) {
|
||||||
print('Authorization error!');
|
print('Authorization error!');
|
||||||
print(response.error);
|
print(token.error);
|
||||||
print(response.error_description);
|
print(token.error_description);
|
||||||
print(response.error_uri);
|
print(token.error_uri);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has granted all the scopes?
|
|
||||||
if (!oauth2.hasGrantedAllScopes(response, scopes[2])) {
|
|
||||||
print('The user has NOT granted the required scope!');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to do a request to the `people` API
|
// Attempt to do a request to the `people` API
|
||||||
final http.Response apiResponse = await http.get(
|
final Object? profile = await get(token, MY_PROFILE);
|
||||||
Uri.parse('https://people.googleapis.com/v1/people/me/connections'
|
print(profile);
|
||||||
'?requestMask.includeField=person.names'),
|
|
||||||
headers: <String, String>{
|
// Has granted all the scopes?
|
||||||
'Authorization': '${response.token_type} ${response.access_token}',
|
if (!oauth2.hasGrantedAllScopes(token, myConnectionsScopes)) {
|
||||||
},
|
print('The user has NOT granted all the required scopes!');
|
||||||
);
|
print('The next get will probably throw an exception!');
|
||||||
if (apiResponse.statusCode == 200) {
|
|
||||||
print('People API ${apiResponse.statusCode} OK!');
|
|
||||||
} else {
|
|
||||||
print(
|
|
||||||
'People API ${apiResponse.statusCode} Oops! Something wrong happened!');
|
|
||||||
}
|
}
|
||||||
print(apiResponse.body);
|
|
||||||
|
final Object? contacts = await get(token, MY_CONNECTIONS);
|
||||||
|
print(contacts);
|
||||||
|
|
||||||
print('Revoking token...');
|
print('Revoking token...');
|
||||||
oauth2.revokeToken(response.access_token, allowInterop((String status) {
|
oauth2.revoke(token.access_token,
|
||||||
print(status);
|
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?;
|
||||||
|
}
|
||||||
|
@ -94,17 +94,24 @@ class TokenClient {
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
requestAccessToken(overridableConfig) {
|
requestAccessToken(overridableConfig) {
|
||||||
this.overridableConfig = overridableConfig;
|
this.config = {...this.config, ...overridableConfig};
|
||||||
let callback = this.overridableConfig.callback || this.config.callback;
|
let callback = this.config.callback;
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callAsync(() => {
|
callAsync(() => {
|
||||||
callback(this.tokenResponse);
|
callback({
|
||||||
|
...this.tokenResponse,
|
||||||
|
scope: this.config.scope,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setMockTokenResponse(tokenResponse) {
|
setMockTokenResponse(access_token) {
|
||||||
this.tokenResponse = tokenResponse;
|
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);
|
return new TokenClient(config);
|
||||||
}
|
}
|
||||||
hasGrantedAllScopes(tokenResponse, scope, ...scopes) {
|
hasGrantedAllScopes(tokenResponse, scope, ...scopes) {
|
||||||
return tokenResponse != null;
|
return tokenResponse != null && !scope.startsWith('not-granted-');
|
||||||
}
|
}
|
||||||
hasGrantedAnyScopes(tokenResponse, scope, ...scopes) {
|
hasGrantedAnyScopes(tokenResponse, scope, ...scopes) {
|
||||||
return tokenResponse != null;
|
return false; // Unused in the lib
|
||||||
}
|
}
|
||||||
revoke(accessToken, done) {
|
revoke(accessToken, done) {
|
||||||
if (!done) {
|
if (!done) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callAsync(() => {
|
callAsync(() => {
|
||||||
done();
|
done({
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockGis() {
|
(function() {
|
||||||
let goog = {
|
let goog = {
|
||||||
accounts: {
|
accounts: {
|
||||||
id: new Id(),
|
id: new Id(),
|
||||||
@ -139,6 +148,4 @@ function mockGis() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
globalThis['google'] = goog;
|
globalThis['google'] = goog;
|
||||||
}
|
}());
|
||||||
|
|
||||||
mockGis();
|
|
||||||
|
@ -88,6 +88,29 @@ extension DomHtmlScriptElementExtension on DomHtmlScriptElement {
|
|||||||
external set defer(bool defer);
|
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)
|
// Trusted Types API (TrustedTypePolicy, TrustedScript, TrustedScriptURL)
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypesAPI
|
// https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypesAPI
|
||||||
|
@ -9,54 +9,142 @@
|
|||||||
// * non_constant_identifier_names required to be able to use the same parameter
|
// * non_constant_identifier_names required to be able to use the same parameter
|
||||||
// names as the underlying library.
|
// names as the underlying library.
|
||||||
|
|
||||||
@JS('google.accounts.id')
|
@JS()
|
||||||
library id;
|
library google_accounts_id;
|
||||||
|
|
||||||
import 'package:js/js.dart';
|
import 'package:js/js.dart';
|
||||||
|
|
||||||
|
import 'dom.dart';
|
||||||
import 'shared.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()
|
@JS()
|
||||||
external SetLogLevelFn get setLogLevel;
|
@staticInterop
|
||||||
|
abstract class GoogleAccountsId {}
|
||||||
|
|
||||||
///
|
/// The `google.accounts.id` methods
|
||||||
typedef SetLogLevelFn = void Function(String level);
|
extension GoogleAccountsIdExtension on GoogleAccountsId {
|
||||||
|
/// An undocumented method.
|
||||||
|
///
|
||||||
|
/// Try it with 'debug'.
|
||||||
|
external void setLogLevel(String level);
|
||||||
|
|
||||||
/*
|
/// Initializes the Sign In With Google client based on [IdConfiguration].
|
||||||
// Method: google.accounts.id.initialize
|
///
|
||||||
// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.initialize
|
/// 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 `prompt` method displays the One Tap prompt or the browser native
|
||||||
///
|
/// credential manager after the [initialize] method is invoked.
|
||||||
/// The `initialize` method creates a Sign In With Google client instance that
|
///
|
||||||
/// can be implicitly used by all modules in the same web page.
|
/// 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
|
||||||
/// * You only need to call the `initialize` method once even if you use
|
/// not be displayed. To get notified on the UI status for different moments,
|
||||||
/// multiple modules (like One Tap, Personalized button, revocation, etc.) in
|
/// pass a [PromptMomentListenerFn] to receive UI status notifications.
|
||||||
/// the same web page.
|
///
|
||||||
/// * If you do call the google.accounts.id.initialize method multiple times,
|
/// Notifications are fired on the following moments:
|
||||||
/// only the configurations in the last call will be remembered and used.
|
///
|
||||||
///
|
/// * Display moment: This occurs after the `prompt` method is called. The
|
||||||
/// You actually reset the configurations whenever you call the `initialize`
|
/// notification contains a boolean value to indicate whether the UI is
|
||||||
/// method, and all subsequent methods in the same web page will use the new
|
/// displayed or not.
|
||||||
/// configurations immediately.
|
/// * 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
|
||||||
/// WARNING: The `initialize` method should be called only once, even if you
|
/// as when the selected session has signed out of Google.
|
||||||
/// use both One Tap and button in the same web page.
|
/// In these cases, we recommend that you continue on to the next identity
|
||||||
@JS()
|
/// providers, if there are any.
|
||||||
external InitializeFn get initialize;
|
/// * 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.
|
/// Renders a Sign In With Google button in your web page.
|
||||||
typedef InitializeFn = void Function(IdConfiguration idConfiguration);
|
///
|
||||||
|
/// 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,
|
||||||
|
]);
|
||||||
|
|
||||||
/*
|
/// Record when the user signs out of your website in cookies.
|
||||||
// Data type: IdConfiguration
|
///
|
||||||
// https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration
|
/// 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.
|
/// The configuration object for the [initialize] method.
|
||||||
|
///
|
||||||
|
/// Data type: IdConfiguration
|
||||||
|
/// https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration
|
||||||
@JS()
|
@JS()
|
||||||
@anonymous
|
@anonymous
|
||||||
@staticInterop
|
@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.
|
/// The type of the function that can be passed to [prompt] to listen for [PromptMomentNotification]s.
|
||||||
typedef PromptMomentListenerFn = void Function(PromptMomentNotification moment);
|
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.
|
/// A moment (status) notification from the [prompt] method.
|
||||||
|
///
|
||||||
|
/// Data type: PromptMomentNotification
|
||||||
|
/// https://developers.google.com/identity/gsi/web/reference/js-reference#PromptMomentNotification
|
||||||
@JS()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class PromptMomentNotification {}
|
abstract class PromptMomentNotification {}
|
||||||
@ -246,25 +292,32 @@ extension PromptMomentNotificationExtension on PromptMomentNotification {
|
|||||||
maybeEnum(_getDismissedReason(), MomentDismissedReason.values);
|
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].
|
/// The object passed as the parameter of your [CallbackFn].
|
||||||
|
///
|
||||||
|
/// Data type: CredentialResponse
|
||||||
|
/// https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse
|
||||||
@JS()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class CredentialResponse {}
|
abstract class CredentialResponse {}
|
||||||
|
|
||||||
/// The fields that are contained in the credential response object.
|
/// The fields that are contained in the credential response object.
|
||||||
extension CredentialResponseExtension on CredentialResponse {
|
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)
|
/// This field is the ID token as a base64-encoded JSON Web Token (JWT)
|
||||||
/// string.
|
/// string.
|
||||||
///
|
///
|
||||||
/// See more: https://developers.google.com/identity/gsi/web/reference/js-reference#credential
|
/// See more: https://developers.google.com/identity/gsi/web/reference/js-reference#credential
|
||||||
external String get credential;
|
external String? get credential;
|
||||||
@JS('select_by')
|
@JS('select_by')
|
||||||
external String get _select_by;
|
external String? get _select_by;
|
||||||
|
|
||||||
/// This field sets how the credential was selected.
|
/// This field sets how the credential was selected.
|
||||||
///
|
///
|
||||||
@ -272,8 +325,8 @@ extension CredentialResponseExtension on CredentialResponse {
|
|||||||
/// to set the value.
|
/// to set the value.
|
||||||
///
|
///
|
||||||
/// See more: https://developers.google.com/identity/gsi/web/reference/js-reference#select_by
|
/// See more: https://developers.google.com/identity/gsi/web/reference/js-reference#select_by
|
||||||
CredentialSelectBy get select_by =>
|
CredentialSelectBy? get select_by =>
|
||||||
CredentialSelectBy.values.byName(_select_by);
|
maybeEnum(_select_by, CredentialSelectBy.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type of the `callback` used to create an [IdConfiguration].
|
/// The type of the `callback` used to create an [IdConfiguration].
|
||||||
@ -285,20 +338,69 @@ extension CredentialResponseExtension on CredentialResponse {
|
|||||||
/// attribute.
|
/// attribute.
|
||||||
typedef CallbackFn = void Function(CredentialResponse credentialResponse);
|
typedef CallbackFn = void Function(CredentialResponse credentialResponse);
|
||||||
|
|
||||||
/*
|
/// The configuration object for the [renderButton] method.
|
||||||
// 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
|
||||||
// Data type: GsiButtonConfiguration
|
@JS()
|
||||||
// https://developers.google.com/identity/gsi/web/reference/js-reference#GsiButtonConfiguration
|
@anonymous
|
||||||
//
|
@staticInterop
|
||||||
// Question: Do we need to implement renderButton and its options?
|
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,
|
||||||
|
|
||||||
/*
|
/// The button theme.
|
||||||
// Data type: Credential
|
ButtonTheme theme,
|
||||||
// https://developers.google.com/identity/gsi/web/reference/js-reference#type-Credential
|
|
||||||
*/
|
/// 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
|
/// The object passed to the [NativeCallbackFn]. Represents a PasswordCredential
|
||||||
/// that was returned by the Browser.
|
/// that was returned by the Browser.
|
||||||
@ -307,6 +409,9 @@ typedef CallbackFn = void Function(CredentialResponse credentialResponse);
|
|||||||
/// in the browser through the [storeCredential] method.
|
/// in the browser through the [storeCredential] method.
|
||||||
///
|
///
|
||||||
/// See also: https://developer.mozilla.org/en-US/docs/Web/API/PasswordCredential/PasswordCredential
|
/// 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()
|
@JS()
|
||||||
@anonymous
|
@anonymous
|
||||||
@staticInterop
|
@staticInterop
|
||||||
@ -333,95 +438,22 @@ extension CredentialExtension on Credential {
|
|||||||
/// from the native Credential manager of the user's browser.
|
/// from the native Credential manager of the user's browser.
|
||||||
typedef NativeCallbackFn = void Function(Credential credential);
|
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
|
// Library load callback: onGoogleLibraryLoad
|
||||||
// https://developers.google.com/identity/gsi/web/reference/js-reference#onGoogleLibraryLoad
|
// https://developers.google.com/identity/gsi/web/reference/js-reference#onGoogleLibraryLoad
|
||||||
// See: `load_callback.dart` and `loader.dart`
|
// 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 type of the `callback` function passed to [revoke], to be notified of
|
||||||
/// the success of the revocation operation.
|
/// the success of the revocation operation.
|
||||||
typedef RevocationResponseHandlerFn = void Function(
|
typedef RevocationResponseHandlerFn = void Function(
|
||||||
RevocationResponse revocationResponse,
|
RevocationResponse revocationResponse,
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/// The parameter passed to the `callback` of the [revoke] function.
|
||||||
// Data type: RevocationResponse
|
///
|
||||||
// https://developers.google.com/identity/gsi/web/reference/js-reference#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.
|
|
||||||
@JS()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class RevocationResponse {}
|
abstract class RevocationResponse {}
|
||||||
|
@ -9,34 +9,84 @@
|
|||||||
// * non_constant_identifier_names required to be able to use the same parameter
|
// * non_constant_identifier_names required to be able to use the same parameter
|
||||||
// names as the underlying library.
|
// names as the underlying library.
|
||||||
|
|
||||||
@JS('google.accounts.oauth2')
|
@JS()
|
||||||
library oauth2;
|
library google_accounts_oauth2;
|
||||||
|
|
||||||
import 'package:js/js.dart';
|
import 'package:js/js.dart';
|
||||||
|
|
||||||
|
import 'dom.dart';
|
||||||
import 'shared.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;
|
||||||
|
|
||||||
/*
|
/// The Dart definition of the `google.accounts.oauth2` global.
|
||||||
// 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].
|
|
||||||
@JS()
|
@JS()
|
||||||
external InitCodeClientFn get initCodeClient;
|
@staticInterop
|
||||||
|
abstract class GoogleAccountsOauth2 {}
|
||||||
|
|
||||||
/// The type of the [initCodeClient] function.
|
/// The `google.accounts.oauth2` methods
|
||||||
typedef InitCodeClientFn = CodeClient Function(CodeClientConfig config);
|
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);
|
||||||
|
|
||||||
/*
|
/// Initializes and returns a token client, with the passed-in [config].
|
||||||
// Data type: CodeClientConfig
|
///
|
||||||
// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClientConfig
|
/// 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.
|
/// The configuration object for the [initCodeClient] method.
|
||||||
|
///
|
||||||
|
/// Data type: CodeClientConfig
|
||||||
|
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClientConfig
|
||||||
@JS()
|
@JS()
|
||||||
@anonymous
|
@anonymous
|
||||||
@staticInterop
|
@staticInterop
|
||||||
@ -51,6 +101,7 @@ abstract class CodeClientConfig {
|
|||||||
String? redirect_uri,
|
String? redirect_uri,
|
||||||
bool? auto_select,
|
bool? auto_select,
|
||||||
CodeClientCallbackFn? callback,
|
CodeClientCallbackFn? callback,
|
||||||
|
ErrorCallbackFn? error_callback,
|
||||||
String? state,
|
String? state,
|
||||||
bool? enable_serial_consent,
|
bool? enable_serial_consent,
|
||||||
String? hint,
|
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.
|
/// A client that can start the OAuth 2.0 Code UX flow.
|
||||||
///
|
///
|
||||||
/// See: https://developers.google.com/identity/oauth2/web/guides/use-code-model
|
/// 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()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class CodeClient {}
|
abstract class CodeClient {}
|
||||||
@ -78,12 +127,10 @@ extension CodeClientExtension on CodeClient {
|
|||||||
external void requestCode();
|
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].
|
/// The object passed as the parameter of your [CodeClientCallbackFn].
|
||||||
|
///
|
||||||
|
/// Data type: CodeResponse
|
||||||
|
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeResponse
|
||||||
@JS()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class CodeResponse {}
|
abstract class CodeResponse {}
|
||||||
@ -116,27 +163,10 @@ extension CodeResponseExtension on CodeResponse {
|
|||||||
/// The type of the `callback` function passed to [CodeClientConfig].
|
/// The type of the `callback` function passed to [CodeClientConfig].
|
||||||
typedef CodeClientCallbackFn = void Function(CodeResponse response);
|
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.
|
/// The configuration object for the [initTokenClient] method.
|
||||||
|
///
|
||||||
|
/// Data type: TokenClientConfig
|
||||||
|
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig
|
||||||
@JS()
|
@JS()
|
||||||
@anonymous
|
@anonymous
|
||||||
@staticInterop
|
@staticInterop
|
||||||
@ -149,6 +179,7 @@ abstract class TokenClientConfig {
|
|||||||
required String client_id,
|
required String client_id,
|
||||||
required TokenClientCallbackFn? callback,
|
required TokenClientCallbackFn? callback,
|
||||||
required String scope,
|
required String scope,
|
||||||
|
ErrorCallbackFn? error_callback,
|
||||||
String? prompt,
|
String? prompt,
|
||||||
bool? enable_serial_consent,
|
bool? enable_serial_consent,
|
||||||
String? hint,
|
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.
|
/// A client that can start the OAuth 2.0 Token UX flow.
|
||||||
///
|
///
|
||||||
/// See: https://developers.google.com/identity/oauth2/web/guides/use-token-model
|
/// 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()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class TokenClient {}
|
abstract class TokenClient {}
|
||||||
@ -177,13 +206,10 @@ extension TokenClientExtension on TokenClient {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// The overridable configuration object for the [TokenClientExtension.requestAccessToken] method.
|
||||||
// Data type: OverridableTokenClientConfig
|
///
|
||||||
// https://developers.google.com/identity/oauth2/web/reference/js-reference#OverridableTokenClientConfig
|
/// Data type: OverridableTokenClientConfig
|
||||||
*/
|
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#OverridableTokenClientConfig
|
||||||
|
|
||||||
/// The overridable configuration object for the
|
|
||||||
/// [TokenClientExtension.requestAccessToken] method.
|
|
||||||
@JS()
|
@JS()
|
||||||
@anonymous
|
@anonymous
|
||||||
@staticInterop
|
@staticInterop
|
||||||
@ -229,15 +255,18 @@ abstract class OverridableTokenClientConfig {
|
|||||||
/// uses to maintain state between your authorization request and the
|
/// uses to maintain state between your authorization request and the
|
||||||
/// authorization server's response.
|
/// authorization server's response.
|
||||||
String? state,
|
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].
|
/// The object passed as the parameter of your [TokenClientCallbackFn].
|
||||||
|
///
|
||||||
|
/// Data type: TokenResponse
|
||||||
|
/// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
|
||||||
@JS()
|
@JS()
|
||||||
@staticInterop
|
@staticInterop
|
||||||
abstract class TokenResponse {}
|
abstract class TokenResponse {}
|
||||||
@ -283,63 +312,55 @@ extension TokenResponseExtension on TokenResponse {
|
|||||||
/// The type of the `callback` function passed to [TokenClientConfig].
|
/// The type of the `callback` function passed to [TokenClientConfig].
|
||||||
typedef TokenClientCallbackFn = void Function(TokenResponse response);
|
typedef TokenClientCallbackFn = void Function(TokenResponse response);
|
||||||
|
|
||||||
/*
|
/// The type of the `error_callback` in both oauth2 initXClient calls.
|
||||||
// Method: google.accounts.oauth2.hasGrantedAllScopes
|
///
|
||||||
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAllScopes
|
/// (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()
|
@JS()
|
||||||
external HasGrantedScopesFn get hasGrantedAllScopes;
|
@staticInterop
|
||||||
|
abstract class GoogleIdentityServicesError extends DomError {}
|
||||||
|
|
||||||
/*
|
/// Methods of the GoogleIdentityServicesError object.
|
||||||
// Method: google.accounts.oauth2.hasGrantedAnyScopes
|
///
|
||||||
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAnyScopes
|
/// 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()
|
@JS()
|
||||||
external HasGrantedScopesFn get hasGrantedAnyScopes;
|
@staticInterop
|
||||||
|
abstract class TokenRevocationResponse {}
|
||||||
|
|
||||||
/// The signature for functions that check if any/all scopes have been granted.
|
/// The fields that are contained in the [TokenRevocationResponse] object.
|
||||||
///
|
extension TokenRevocationResponseExtension on TokenRevocationResponse {
|
||||||
/// Used by [hasGrantedAllScopes] and [hasGrantedAnyScope].
|
/// This field is a boolean value set to true if the revoke method call
|
||||||
typedef HasGrantedScopesFn = bool Function(
|
/// succeeded or false on failure.
|
||||||
TokenResponse tokenResponse,
|
external bool get successful;
|
||||||
String firstScope, [
|
|
||||||
String? scope2,
|
|
||||||
String? scope3,
|
|
||||||
String? scope4,
|
|
||||||
String? scope5,
|
|
||||||
String? scope6,
|
|
||||||
String? scope7,
|
|
||||||
String? scope8,
|
|
||||||
String? scope9,
|
|
||||||
String? scope10,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/*
|
/// This field is a string value and contains a detailed error message if the
|
||||||
// Method: google.accounts.oauth2.revoke
|
/// revoke method call failed, it is undefined on success.
|
||||||
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.revoke
|
external String? get error;
|
||||||
*/
|
|
||||||
|
|
||||||
/// The [revokeToken] method revokes all of the scopes that the user granted to
|
/// The description of the error.
|
||||||
/// the app. A valid access token is required to revoke the permission.
|
external String? get error_description;
|
||||||
///
|
}
|
||||||
/// 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);
|
|
||||||
|
@ -213,3 +213,159 @@ enum CredentialSelectBy {
|
|||||||
@override
|
@override
|
||||||
String toString() => _selectBy;
|
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;
|
||||||
|
}
|
||||||
|
@ -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.
|
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
|
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
|
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:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
# Tests
|
# Tests
|
||||||
|
|
||||||
Use `dart run tool/run_tests.dart` to run tests in this package.
|
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).
|
||||||
|
Reference in New Issue
Block a user