[gis_web] Introduce new GIS SDK JS-Interop package. (#2653)

This commit is contained in:
David Iglesias
2022-10-06 17:10:04 -07:00
committed by GitHub
parent cd33647475
commit 6ee03370cf
42 changed files with 2093 additions and 6 deletions

View File

@ -5,9 +5,9 @@
# Exclusions
#
# cross_file
# script/configs/linux_only_custom_test.yaml
# Custom tests need Chrome. (They run in linux-custom_package_tests)
dart pub global run flutter_plugin_tools custom-test \
--packages-for-branch --log-timing \
--exclude=cross_file
--exclude=script/configs/linux_only_custom_test.yaml

View File

@ -261,12 +261,12 @@ task:
local_tests_script:
# flutter_image
# https://github.com/flutter/flutter/issues/100387
# cross_file
# Custom tests need Chrome. (They run in linux-custom_package_tests)
# script/configs/linux_only_custom_test.yaml
# Custom tests need Chrome for these packages. (They run in linux-custom_package_tests)
- if [[ "$CHANNEL" == "master" ]]; then
- ./script/tool_runner.sh custom-test --exclude=cross_file
- ./script/tool_runner.sh custom-test --exclude=script/configs/linux_only_custom_test.yaml
- else
- ./script/tool_runner.sh custom-test --exclude=cross_file,flutter_image
- ./script/tool_runner.sh custom-test --exclude=flutter_image,script/configs/linux_only_custom_test.yaml
- fi
### macOS desktop tasks ###
- name: macos-platform_tests

3
.github/labeler.yml vendored
View File

@ -37,6 +37,9 @@
'p: go_router_builder':
- packages/go_router_builder/**/*
'p: google_identity_services':
- packages/google_indentity_services_web/**
'p: imitation_game':
- packages/imitation_game/**/*

View File

@ -0,0 +1,7 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>
Google Inc.
The Chromium Authors

View File

@ -0,0 +1,3 @@
## 0.1.0
* Initial release.

View File

@ -0,0 +1,25 @@
Copyright 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,87 @@
<?code-excerpt path-base="excerpts/packages/google_identity_services_web_example"?>
# google_identity_services_web
A JS-interop layer for Google Identity's Sign In With Google SDK.
See the original JS SDK reference:
* [Sign In With Google](https://developers.google.com/identity/gsi/web)
## Usage
This package is the Dart JS-interop layer of the new **Sign In With Google**
SDK. Here's the API references for both of the sub-libraries:
* `id.dart`: [Sign In With Google JavaScript API reference](https://developers.google.com/identity/gsi/web/reference/js-reference)
* `oauth2.dart`: [Google 3P Authorization JavaScript Library for websites - API reference](https://developers.google.com/identity/oauth2/web/reference/js-reference)
* `loader.dart`: An (optional) loader mechanism that installs the library and
resolves a `Future<void>` when it's ready.
### Loading the SDK
There are two ways to load the JS SDK in your app.
#### Modify your index.html (most performant)
The most performant way is to modify your `web/index.html` file to insert a
script tag [as recommended](https://developers.google.com/identity/gsi/web/guides/client-library).
Place the `script` tag in the `<head>` of your site, next to the script tag that
loads `flutter.js`, so the browser can downloaded both in parallel:
<?code-excerpt "../../web/index-with-script-tag.html (script-tag)"?>
```html
<head>
<!-- ··· -->
<!-- Include the GSI SDK below -->
<script src="https://accounts.google.com/gsi/client" async defer></script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
```
#### With the `loadWebSdk` function (on-demand)
An alternative way, that downloads the SDK on demand, is to use the
**`loadWebSdk`** function provided by the library. A simple location to embed
this in a Flutter Web only app can be the `main.dart`:
<?code-excerpt "main.dart (use-loader)"?>
```dart
import 'package:google_identity_services_web/loader.dart' as gis;
// ···
void main() async {
await gis.loadWebSdk(); // Load the GIS SDK
// The rest of your code...
// ···
}
```
(Note that the above won't compile for mobile apps, so if you're developing a
cross-platform app, you'll probably need to hide the call to `loadWebSdk`
behind a [conditional import/export](https://dart.dev/guides/libraries/create-library-packages#conditionally-importing-and-exporting-library-files).)
### Using the SDK
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.
## Browser compatibility
The new SDK is introducing concepts that are on track for standardization to
most browsers, and it might not be compatible with older browsers.
Refer to the official documentation site for the latest browser compatibility
information of the underlying JS SDK:
* **Sign In With Google > [Supported browsers and platforms](https://developers.google.com/identity/gsi/web/guides/supported-browsers)**
## Testing
This web-only package uses `dart:test` to test its features. They can be run
with `dart test -p chrome`.
_(Look at `test/README.md` and `tool/run_tests.dart` for more info.)_

View File

@ -0,0 +1,6 @@
# See https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md#arguments
override_platforms:
chrome:
settings:
executable: chrome
arguments: --no-sandbox

View File

@ -0,0 +1,44 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View File

@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
channel: master
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
- platform: android
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
- platform: ios
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
- platform: linux
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
- platform: macos
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
- platform: web
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
- platform: windows
create_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
base_revision: 822f6e3595d63f864ae2027ea37fd645b313b71b
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -0,0 +1,4 @@
# google_identity_services_web_example
* `lib/main.dart`: An example on how to use the google_identiy_services_web `id` library from Dart.
* `lib/main_oauth.dart`: An example for the `oauth2` library from the same SDK.

View File

@ -0,0 +1,19 @@
targets:
$default:
sources:
include:
- lib/**
- web/**
# Some default includes that aren't really used here but will prevent
# false-negative warnings:
- $package$
- lib/$lib$
exclude:
- '**/.*/**'
- '**/build/**'
builders:
code_excerpter|code_excerpter:
enabled: true
generate_for:
- '**/*.dart'
- '**/*.html'

View File

@ -0,0 +1,56 @@
// 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.
// ignore_for_file: avoid_print
import 'package:google_identity_services_web/id.dart' as id;
// #docregion use-loader
import 'package:google_identity_services_web/loader.dart' as gis;
// #enddocregion use-loader
import 'package:js/js.dart' show allowInterop;
import 'package:jwt_decoder/jwt_decoder.dart' as jwt;
// #docregion use-loader
void main() async {
await gis.loadWebSdk(); // Load the GIS SDK
// The rest of your code...
// #enddocregion use-loader
id.setLogLevel('debug');
final id.IdConfiguration config = id.IdConfiguration(
client_id: 'your-client_id.apps.googleusercontent.com',
ux_mode: id.UxMode.popup,
callback: allowInterop(onCredentialResponse),
);
id.initialize(config);
id.prompt(allowInterop(onPromptMoment));
// #docregion use-loader
}
// #enddocregion use-loader
/// 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);
if (payload != null) {
print('Hello, ${payload["name"]}');
print(o.select_by);
print(payload);
} else {
print('Could not decode ${o.credential}');
}
}
/// 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();
print(type.runtimeType);
print(type);
print(type.index);
print(o.getDismissedReason());
print(o.getNotDisplayedReason());
print(o.getSkippedReason());
}

View File

@ -0,0 +1,79 @@
// 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.
// ignore_for_file: avoid_print
import 'package:google_identity_services_web/id.dart' as id show setLogLevel;
import 'package:google_identity_services_web/loader.dart' as gis;
import 'package:google_identity_services_web/oauth2.dart' as oauth2;
import 'package:http/http.dart' as http;
import 'package:js/js.dart' show allowInterop;
/// The scopes to be requested
const List<String> scopes = <String>[
'email',
'profile',
'https://www.googleapis.com/auth/contacts.readonly',
];
void main() async {
await gis.loadWebSdk(); // Load the GIS SDK
id.setLogLevel('debug');
final oauth2.TokenClientConfig config = oauth2.TokenClientConfig(
client_id: 'your-client_id.apps.googleusercontent.com',
scope: scopes.join(' '),
callback: allowInterop(onTokenResponse),
);
final oauth2.OverridableTokenClientConfig overridableCfg =
oauth2.OverridableTokenClientConfig(
prompt: '',
);
final oauth2.TokenClient client = oauth2.initTokenClient(config);
// Disable the Popup Blocker for this to work, or move this to a Button press.
client.requestAccessToken(overridableCfg);
}
/// 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) {
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!');
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!');
}
print(apiResponse.body);
print('Revoking token...');
oauth2.revokeToken(response.access_token, allowInterop((String status) {
print(status);
}));
}

View File

@ -0,0 +1,24 @@
name: google_identity_services_web_example
description: An example for the google_identity_services_web package, OneTap.
publish_to: 'none'
version: 0.0.1
environment:
sdk: '>=2.17.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
google_identity_services_web:
path: ../
http: ^0.13.5
js: ^0.6.4
jwt_decoder: ^2.0.1
dev_dependencies:
build_runner: ^2.1.10 # To extract README excerpts only.
flutter_test:
sdk: flutter
flutter:
uses-material-design: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<!-- 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. -->
<html>
<!--#docregion script-tag-->
<head>
<!--#enddocregion script-tag-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>Authentication Example</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!--#docregion script-tag-->
<!-- Include the GSI SDK below -->
<script src="https://accounts.google.com/gsi/client" async defer></script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<!--#enddocregion script-tag-->
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.autoStart();
}
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<!-- 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. -->
<html>
<head>
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>Authentication Example</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- Trusted Types API config (disabled) -->
<!-- <meta http-equiv="Content-Security-Policy" content="trusted-types;"> -->
<!-- Load the GSI SDK -->
<!-- <script src="https://accounts.google.com/gsi/client" async defer></script> -->
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.autoStart();
}
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,35 @@
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

View File

@ -0,0 +1,5 @@
// 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.
export 'loader.dart';

View File

@ -0,0 +1,14 @@
// 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.
export 'src/js_interop/google_accounts_id.dart';
export 'src/js_interop/shared.dart'
show
CredentialSelectBy,
MomentDismissedReason,
MomentNotDisplayedReason,
MomentSkippedReason,
MomentType,
OneTapContext,
UxMode;

View File

@ -0,0 +1,5 @@
// 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.
export 'src/js_loader.dart' show TrustedTypesException, loadWebSdk;

View File

@ -0,0 +1,6 @@
// 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.
export 'src/js_interop/google_accounts_oauth2.dart';
export 'src/js_interop/shared.dart' show UxMode;

View File

@ -0,0 +1,164 @@
// 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.
/*
// DOM shim. This file contains everything we need from the DOM API written as
// @staticInterop, so we don't need dart:html
// https://developer.mozilla.org/en-US/docs/Web/API/
*/
import 'package:js/js.dart';
/// Document interface
@JS()
@staticInterop
@anonymous
abstract class DomHtmlDocument {}
/// Some methods of document
extension DomHtmlDocumentExtension on DomHtmlDocument {
/// document.head
external DomHtmlElement get head;
/// document.createElement
external DomHtmlElement createElement(String tagName);
}
/// console interface
@JS()
@staticInterop
@anonymous
abstract class DomConsole {}
/// The interface of window.console
extension DomConsoleExtension on DomConsole {
/// console.debug
external DomConsoleDumpFn get debug;
/// console.info
external DomConsoleDumpFn get info;
/// console.log
external DomConsoleDumpFn get log;
/// console.warn
external DomConsoleDumpFn get warn;
/// console.error
external DomConsoleDumpFn get error;
}
/// Fakey variadic-type for console-dumping methods (like console.log or info).
typedef DomConsoleDumpFn = void Function(
Object? arg, [
Object? arg2,
Object? arg3,
Object? arg4,
Object? arg5,
Object? arg6,
Object? arg7,
Object? arg8,
Object? arg9,
Object? arg10,
]);
/// An instance of an HTMLElement
@JS()
@staticInterop
@anonymous
abstract class DomHtmlElement {}
/// (Some) methods of HtmlElement
extension DomHtmlElementExtension on DomHtmlElement {
/// Node.appendChild
external DomHtmlElement appendChild(DomHtmlElement child);
}
/// An instance of an HTMLScriptElement
@JS()
@staticInterop
@anonymous
abstract class DomHtmlScriptElement extends DomHtmlElement {}
/// Some methods exclusive of Script elements
extension DomHtmlScriptElementExtension on DomHtmlScriptElement {
external set src(Object stringOrSafeScriptURL);
external set async(bool async);
external set defer(bool defer);
}
/*
// Trusted Types API (TrustedTypePolicy, TrustedScript, TrustedScriptURL)
// https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypesAPI
*/
/// A factory to create `TrustedTypePolicy` objects.
@JS()
@staticInterop
@anonymous
abstract class DomTrustedTypePolicyFactory {}
/// (Some) methods of the [DomTrustedTypePolicyFactory]:
extension DomTrustedTypePolicyFactoryExtension on DomTrustedTypePolicyFactory {
/// createPolicy
external DomTrustedTypePolicy createPolicy(
String policyName,
DomTrustedTypePolicyOptions? policyOptions,
);
}
/// Options to create a trusted type policy.
@JS()
@staticInterop
@anonymous
abstract class DomTrustedTypePolicyOptions {
/// Constructs a TrustedPolicyOptions object in JavaScript.
///
/// The following properties need to be manually wrapped in [allowInterop]
/// before being passed to this constructor: [createScriptURL].
external factory DomTrustedTypePolicyOptions({
DomCreateScriptUrlOptionFn? createScriptURL,
});
}
/// Type of the function to configure createScriptURL
typedef DomCreateScriptUrlOptionFn = String Function(String input);
/// An instance of a TrustedTypePolicy
@JS()
@staticInterop
@anonymous
abstract class DomTrustedTypePolicy {}
/// (Some) methods of the [DomTrustedTypePolicy]
extension DomTrustedTypePolicyExtension on DomTrustedTypePolicy {
/// Create a `TrustedScriptURL` for the given [input].
external DomTrustedScriptUrl createScriptURL(String input);
}
/// An instance of a DomTrustedScriptUrl
@JS()
@staticInterop
@anonymous
abstract class DomTrustedScriptUrl {}
// Getters
/// window.document
@JS()
@staticInterop
@anonymous
external DomHtmlDocument get document;
/// window.trustedTypes (may or may not be supported by the browser)
@JS()
@staticInterop
@anonymous
external DomTrustedTypePolicyFactory? get trustedTypes;
/// window.console
@JS()
@staticInterop
@anonymous
external DomConsole get console;

View File

@ -0,0 +1,440 @@
// 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.
// Authentication. API reference:
// https://developers.google.com/identity/gsi/web/reference/js-reference
// ignore_for_file: non_constant_identifier_names
// * non_constant_identifier_names required to be able to use the same parameter
// names as the underlying library.
@JS('google.accounts.id')
library id;
import 'package:js/js.dart';
import 'shared.dart';
/// An undocumented method. Try with 'debug'.
@JS()
external SetLogLevelFn get setLogLevel;
///
typedef SetLogLevelFn = void Function(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.
@JS()
external InitializeFn get initialize;
/// The type of the [initialize] function.
typedef InitializeFn = void Function(IdConfiguration idConfiguration);
/*
// Data type: IdConfiguration
// https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration
*/
/// The configuration object for the [initialize] method.
@JS()
@anonymous
@staticInterop
abstract class IdConfiguration {
/// Constructs a IdConfiguration object in JavaScript.
///
/// The following properties need to be manually wrapped in [allowInterop]
/// before being passed to this constructor: [callback], [native_callback],
/// and [intermediate_iframe_close_callback].
external factory IdConfiguration({
/// Your application's client ID, which is found and created in the Google
/// Developers Console.
required String client_id,
/// Determines if an ID token is automatically returned without any user
/// interaction when there's only one Google session that has approved your
/// app before. The default value is `false`.
bool? auto_select,
/// The function that handles the ID token returned from the One Tap prompt
/// or the pop-up window. This attribute is required if Google One Tap or
/// the Sign In With Google button `popup` UX mode is used.
CallbackFn? callback,
/// This attribute is the URI of your login endpoint. May be omitted if the
/// current page is your login page, in which case the credential is posted
/// to this page by default.
///
/// The ID token credential response is posted to your login endpoint when
/// a user clicks on the Sign In With Google button and `redirect` UX mode
/// is used.
///
/// Your login endpoint must handle POST requests containing a credential
/// key with an ID token value in the body.
Uri? login_uri,
/// The function that handles the password credential returned from the
/// browser's native credential manager.
NativeCallbackFn? native_callback,
/// Whether or not to cancel the One Tap request if a user clicks outside
/// the prompt. The default value is `true`.
bool? cancel_on_tap_outside,
/// The DOM ID of the container element. If it's not set, the One Tap prompt
/// is displayed in the top-right corner of the window.
String? prompt_parent_id,
/// A random string used by the ID token to prevent replay attacks.
///
/// Nonce length is limited to the maximum JWT size supported by your
/// environment, and individual browser and server HTTP size constraints.
String? nonce,
/// Changes the text of the title and messages in the One Tap prompt.
OneTapContext? context,
/// If you need to display One Tap in the parent domain and its subdomains,
/// pass the parent domain to this field so that a single shared-state
/// cookie is used.
///
/// See: https://developers.google.com/identity/gsi/web/guides/subdomains
String? state_cookie_domain,
/// Set the UX flow used by the Sign In With Google button. The default
/// value is `popup`. **This attribute has no impact on the OneTap UX.**
UxMode? ux_mode,
/// The origins that are allowed to embed the intermediate iframe. One Tap
/// will run in the intermediate iframe mode if this field presents.
///
/// Wildcard prefixes are also supported. Wildcard domains must begin with
/// a secure `https://` scheme, otherwise they'll be considered invalid.
List<String>? allowed_parent_origin,
/// Overrides the default intermediate iframe behavior when users manually
/// close One Tap by tapping on the 'X' button in the One Tap UI. The
/// default behavior is to remove the intermediate iframe from the DOM
/// immediately.
///
/// The `intermediate_iframe_close_callback` field takes effect only in
/// intermediate iframe mode. And it has impact only to the intermediate
/// iframe, instead of the One Tap iframe. The One Tap UI is removed before
/// the callback is invoked.
Function? intermediate_iframe_close_callback,
/// determines if the upgraded One Tap UX should be enabled on browsers
/// that support Intelligent Tracking Prevention (ITP). The default value
/// is false.
///
/// See: https://developers.google.com/identity/gsi/web/guides/features#upgraded_ux_on_itp_browsers
bool? itp_support,
});
}
/*
// 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.
@JS()
@staticInterop
abstract class PromptMomentNotification {}
/// The methods of the [PromptMomentNotification] data type:
extension PromptMomentNotificationExtension on PromptMomentNotification {
/// Is this notification for a display moment?
external bool isDisplayMoment();
/// Is this notification for a display moment, and the UI is displayed?
external bool isDisplayed();
/// Is this notification for a display moment, and the UI isn't displayed?
external bool isNotDisplayed();
/// Is this notification for a skipped moment?
external bool isSkippedMoment();
/// Is this notification for a dismissed moment?
external bool isDismissedMoment();
@JS('getMomentType')
external String _getMomentType();
@JS('getNotDisplayedReason')
external String? _getNotDisplayedReason();
@JS('getSkippedReason')
external String? _getSkippedReason();
@JS('getDismissedReason')
external String? _getDismissedReason();
/// The moment type.
MomentType getMomentType() => MomentType.values.byName(_getMomentType());
/// The detailed reason why the UI isn't displayed.
MomentNotDisplayedReason? getNotDisplayedReason() =>
maybeEnum(_getNotDisplayedReason(), MomentNotDisplayedReason.values);
/// The detailed reason for the skipped moment.
MomentSkippedReason? getSkippedReason() =>
maybeEnum(_getSkippedReason(), MomentSkippedReason.values);
/// The detailed reason for the dismissal.
MomentDismissedReason? getDismissedReason() =>
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].
@JS()
@staticInterop
abstract class CredentialResponse {}
/// The fields that are contained in the credential response object.
extension CredentialResponseExtension on CredentialResponse {
/// 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;
@JS('select_by')
external String get _select_by;
/// This field sets how the credential was selected.
///
/// The type of button used along with the session and consent state are used
/// 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);
}
/// The type of the `callback` used to create an [IdConfiguration].
///
/// Describes a JavaScript function that handles ID tokens from
/// [CredentialResponse]s.
///
/// Google One Tap and the Sign In With Google button popup UX mode use this
/// 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?
*/
/*
// Data type: Credential
// https://developers.google.com/identity/gsi/web/reference/js-reference#type-Credential
*/
/// The object passed to the [NativeCallbackFn]. Represents a PasswordCredential
/// that was returned by the Browser.
///
/// `Credential` objects can also be programmatically created to be stored
/// in the browser through the [storeCredential] method.
///
/// See also: https://developer.mozilla.org/en-US/docs/Web/API/PasswordCredential/PasswordCredential
@JS()
@anonymous
@staticInterop
abstract class Credential {
///
external factory Credential({
required String id,
required String password,
});
}
/// The fields that are contained in the [Credential] object.
extension CredentialExtension on Credential {
/// Identifies the user.
external String get id;
/// The password.
external String get password;
}
/// The type of the `native_callback` used to create an [IdConfiguration].
///
/// Describes a JavaScript function that handles password [Credential]s coming
/// 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.
@JS()
@staticInterop
abstract class RevocationResponse {}
/// The fields that are contained in the [RevocationResponse] object.
extension RevocationResponseExtension on RevocationResponse {
/// This field is a boolean value set to true if the revoke method call
/// succeeded or false on failure.
external bool get successful;
/// 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;
}

View File

@ -0,0 +1,339 @@
// 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.
// Authorization. API reference:
// https://developers.google.com/identity/oauth2/web/reference/js-reference
// ignore_for_file: non_constant_identifier_names
// * non_constant_identifier_names required to be able to use the same parameter
// names as the underlying library.
@JS('google.accounts.oauth2')
library oauth2;
import 'package:js/js.dart';
import 'shared.dart';
// Code Client
/*
// 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()
external InitCodeClientFn get initCodeClient;
/// The type of the [initCodeClient] function.
typedef InitCodeClientFn = CodeClient Function(CodeClientConfig config);
/*
// Data type: CodeClientConfig
// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClientConfig
*/
/// The configuration object for the [initCodeClient] method.
@JS()
@anonymous
@staticInterop
abstract class CodeClientConfig {
/// Constructs a CodeClientConfig object in JavaScript.
///
/// The [callback] property must be wrapped in [allowInterop] before it's
/// passed to this constructor.
external factory CodeClientConfig({
required String client_id,
required String scope,
String? redirect_uri,
bool? auto_select,
CodeClientCallbackFn? callback,
String? state,
bool? enable_serial_consent,
String? hint,
String? hosted_domain,
UxMode? ux_mode,
bool? select_account,
});
}
/*
// 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
@JS()
@staticInterop
abstract class CodeClient {}
/// The methods available on the [CodeClient].
extension CodeClientExtension on CodeClient {
/// Starts the OAuth 2.0 Code UX flow.
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].
@JS()
@staticInterop
abstract class CodeResponse {}
/// The fields that are contained in the code response object.
extension CodeResponseExtension on CodeResponse {
/// The authorization code of a successful token response.
external String get code;
/// A space-delimited list of scopes that are approved by the user.
external String get scope;
/// The string value that your application uses to maintain state between your
/// authorization request and the response.
external String get state;
/// A single ASCII error code.
external String? get error;
/// Human-readable ASCII text providing additional information, used to assist
/// the client developer in understanding the error that occurred.
external String? get error_description;
/// A URI identifying a human-readable web page with information about the
/// error, used to provide the client developer with additional information
/// about the error.
external String? get error_uri;
}
/// 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.
@JS()
@anonymous
@staticInterop
abstract class TokenClientConfig {
/// Constructs a TokenClientConfig object in JavaScript.
///
/// The [callback] property must be wrapped in [allowInterop] before it's
/// passed to this constructor.
external factory TokenClientConfig({
required String client_id,
required TokenClientCallbackFn? callback,
required String scope,
String? prompt,
bool? enable_serial_consent,
String? hint,
String? hosted_domain,
String? state,
});
}
/*
// 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
@JS()
@staticInterop
abstract class TokenClient {}
/// The methods available on the [TokenClient].
extension TokenClientExtension on TokenClient {
/// Starts the OAuth 2.0 Code UX flow.
external void requestAccessToken([
OverridableTokenClientConfig overrideConfig,
]);
}
/*
// Data type: OverridableTokenClientConfig
// https://developers.google.com/identity/oauth2/web/reference/js-reference#OverridableTokenClientConfig
*/
/// The overridable configuration object for the
/// [TokenClientExtension.requestAccessToken] method.
@JS()
@anonymous
@staticInterop
abstract class OverridableTokenClientConfig {
/// Constructs an OverridableTokenClientConfig object in JavaScript.
///
/// The [callback] property must be wrapped in [allowInterop] before it's
/// passed to this constructor.
external factory OverridableTokenClientConfig({
/// A space-delimited, case-sensitive list of prompts to present the user.
///
/// See `prompt` in [TokenClientConfig].
String? prompt,
/// For clients created before 2019, when set to `false`, disables "more
/// granular Google Account permissions".
///
/// This setting has no effect in newer clients.
///
/// See: https://developers.googleblog.com/2018/10/more-granular-google-account.html
bool? enable_serial_consent,
/// When your app knows which user it is trying to authenticate, it can
/// provide this parameter as a hint to the authentication server. Passing
/// this hint suppresses the account chooser and either pre-fills the email
/// box on the sign-in form, or selects the proper session (if the user is
/// using multiple sign-in), which can help you avoid problems that occur if
/// your app logs in the wrong user account.
///
/// The value can be either an email address or the `sub` string, which is
/// equivalent to the user's Google ID.
///
/// About Multiple Sign-in: https://support.google.com/accounts/answer/1721977
String? hint,
/// **Not recommended.** Specifies any string value that your application
/// uses to maintain state between your authorization request and the
/// authorization server's response.
String? state,
});
}
/*
// Data type: TokenResponse
// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse
*/
/// The object passed as the parameter of your [TokenClientCallbackFn].
@JS()
@staticInterop
abstract class TokenResponse {}
/// The fields that are contained in the code response object.
extension TokenResponseExtension on TokenResponse {
/// The access token of a successful token response.
external String get access_token;
/// The lifetime in seconds of the access token.
external int get expires_in;
/// The hosted domain the signed-in user belongs to.
external String get hd;
/// The prompt value that was used from the possible list of values specified
/// by [TokenClientConfig] or [OverridableTokenClientConfig].
external String get prompt;
/// The type of the token issued.
external String get token_type;
/// A space-delimited list of scopes that are approved by the user.
external String get scope;
/// The string value that your application uses to maintain state between your
/// authorization request and the response.
external String get state;
/// A single ASCII error code.
external String? get error;
/// Human-readable ASCII text providing additional information, used to assist
/// the client developer in understanding the error that occurred.
external String? get error_description;
/// A URI identifying a human-readable web page with information about the
/// error, used to provide the client developer with additional information
/// about the error.
external String? get error_uri;
}
/// 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
*/
/// Checks if the user granted **all** the specified scopes.
@JS()
external HasGrantedScopesFn get hasGrantedAllScopes;
/*
// Method: google.accounts.oauth2.hasGrantedAnyScopes
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.hasGrantedAnyScopes
*/
/// Checks if the user granted **any** of the specified scopes.
@JS()
external HasGrantedScopesFn get hasGrantedAnyScopes;
/// 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,
]);
/*
// Method: google.accounts.oauth2.revoke
// https://developers.google.com/identity/oauth2/web/reference/js-reference#google.accounts.oauth2.revoke
*/
/// 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);

View File

@ -0,0 +1,26 @@
// 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.
// Methods here are documented in the Google Identity authentication website,
// but they don't really belong to either the authentication nor authorization
// libraries.
@JS()
library id_load_callback;
import 'package:js/js.dart';
import 'shared.dart';
/*
// Library load callback: onGoogleLibraryLoad
// https://developers.google.com/identity/gsi/web/reference/js-reference#onGoogleLibraryLoad
*/
/// Method called after the Sign In With Google JavaScript library is loaded.
///
/// The [callback] parameter must be manually wrapped in [allowInterop]
/// before being set to the [onGoogleLibraryLoad] property.
@JS()
@staticInterop
external set onGoogleLibraryLoad(VoidFn callback);

View File

@ -0,0 +1,215 @@
// 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.
/// Attempts to retrieve an enum value from [haystack] if [needle] is not null.
T? maybeEnum<T extends Enum>(String? needle, List<T> haystack) {
if (needle == null) {
return null;
}
return haystack.byName(needle);
}
/// The type of several functions from the library, that don't receive
/// parameters nor return anything.
typedef VoidFn = void Function();
/*
// Enum: UX Mode
// https://developers.google.com/identity/gsi/web/reference/js-reference#ux_mode
// Used both by `oauth2.initCodeClient` and `id.initialize`.
*/
/// Use this enum to set the UX flow used by the Sign In With Google button.
/// The default value is [popup].
///
/// This attribute has no impact on the OneTap UX.
enum UxMode {
/// Performs sign-in UX flow in a pop-up window.
popup('popup'),
/// Performs sign-in UX flow by a full page redirection.
redirect('redirect');
///
const UxMode(String uxMode) : _uxMode = uxMode;
final String _uxMode;
@override
String toString() => _uxMode;
}
/// Changes the text of the title and messages in the One Tap prompt.
enum OneTapContext {
/// "Sign in with Google"
signin('signin'),
/// "Sign up with Google"
signup('signup'),
/// "Use with Google"
use('use');
///
const OneTapContext(String context) : _context = context;
final String _context;
@override
String toString() => _context;
}
/// The detailed reason why the OneTap UI isn't displayed.
enum MomentNotDisplayedReason {
/// Browser not supported.
///
/// See https://developers.google.com/identity/gsi/web/guides/supported-browsers
browser_not_supported('browser_not_supported'),
/// Invalid Client.
///
/// See https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid
invalid_client('invalid_client'),
/// Missing client_id.
///
/// See https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid
missing_client_id('missing_client_id'),
/// The user has opted out, or they aren't signed in to a Google account.
///
/// https://developers.google.com/identity/gsi/web/guides/features
opt_out_or_no_session('opt_out_or_no_session'),
/// Google One Tap can only be displayed in HTTPS domains.
///
/// See https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid
secure_http_required('secure_http_required'),
/// The user has previously closed the OneTap card.
///
/// See https://developers.google.com/identity/gsi/web/guides/features#exponential_cooldown
suppressed_by_user('suppressed_by_user'),
/// The current `origin` is not associated with the Client ID.
///
/// See https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid
unregistered_origin('unregistered_origin'),
/// Unknown reason
unknown_reason('unknown_reason');
///
const MomentNotDisplayedReason(String reason) : _reason = reason;
final String _reason;
@override
String toString() => _reason;
}
/// The detailed reason for the skipped moment.
enum MomentSkippedReason {
/// auto_cancel
auto_cancel('auto_cancel'),
/// user_cancel
user_cancel('user_cancel'),
/// tap_outside
tap_outside('tap_outside'),
/// issuing_failed
issuing_failed('issuing_failed');
///
const MomentSkippedReason(String reason) : _reason = reason;
final String _reason;
@override
String toString() => _reason;
}
/// The detailed reason for the dismissal.
enum MomentDismissedReason {
/// credential_returned
credential_returned('credential_returned'),
/// cancel_called
cancel_called('cancel_called'),
/// flow_restarted
flow_restarted('flow_restarted');
///
const MomentDismissedReason(String reason) : _reason = reason;
final String _reason;
@override
String toString() => _reason;
}
/// The moment type.
enum MomentType {
/// Display moment
display('display'),
/// Skipped moment
skipped('skipped'),
/// Dismissed moment
dismissed('dismissed');
///
const MomentType(String type) : _type = type;
final String _type;
@override
String toString() => _type;
}
/// Represents how a credential was selected.
enum CredentialSelectBy {
/// Automatic sign-in of a user with an existing session who had previously
/// granted consent to share credentials.
auto('auto'),
/// A user with an existing session who had previously granted consent
/// pressed the One Tap 'Continue as' button to share credentials.
user('user'),
/// A user with an existing session pressed the One Tap 'Continue as' button
/// to grant consent and share credentials. Applies only to Chrome v75 and
/// higher.
user_1tap('user_1tap'),
/// A user without an existing session pressed the One Tap 'Continue as'
/// button to select an account and then pressed the Confirm button in a
/// pop-up window to grant consent and share credentials. Applies to
/// non-Chromium based browsers.
user_2tap('user_2tap'),
/// A user with an existing session who previously granted consent pressed
/// the Sign In With Google button and selected a Google Account from
/// 'Choose an Account' to share credentials.
btn('btn'),
/// A user with an existing session pressed the Sign In With Google button
/// and pressed the Confirm button to grant consent and share credentials.
btn_confirm('btn_confirm'),
/// A user without an existing session who previously granted consent
/// pressed the Sign In With Google button to select a Google Account and
/// share credentials.
btn_add_session('btn_add_session'),
/// A user without an existing session first pressed the Sign In With Google
/// button to select a Google Account and then pressed the Confirm button to
/// consent and share credentials.
btn_confirm_add_session('btn_confirm_add_session');
///
const CredentialSelectBy(String selectBy) : _selectBy = selectBy;
final String _selectBy;
@override
String toString() => _selectBy;
}

View File

@ -0,0 +1,67 @@
// 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:js/js.dart' show allowInterop;
import 'js_interop/dom.dart';
import 'js_interop/load_callback.dart';
// The URL from which the script should be downloaded.
const String _url = 'https://accounts.google.com/gsi/client';
// The default TrustedPolicy name that will be used to inject the script.
const String _defaultTrustedPolicyName = 'gis-dart';
/// Loads the GIS SDK for web, using Trusted Types API when available.
Future<void> loadWebSdk({
DomHtmlElement? target,
String trustedTypePolicyName = _defaultTrustedPolicyName,
}) {
final Completer<void> completer = Completer<void>();
onGoogleLibraryLoad = allowInterop(() => completer.complete());
// If TrustedTypes are available, prepare a trusted URL.
DomTrustedScriptUrl? trustedUrl;
if (trustedTypes != null) {
console.debug(
'TrustedTypes available. Creating policy:',
trustedTypePolicyName,
);
final DomTrustedTypePolicyFactory factory = trustedTypes!;
try {
final DomTrustedTypePolicy policy = factory.createPolicy(
trustedTypePolicyName,
DomTrustedTypePolicyOptions(
createScriptURL: allowInterop((String url) => _url),
));
trustedUrl = policy.createScriptURL(_url);
} catch (e) {
throw TrustedTypesException(e.toString());
}
}
final DomHtmlScriptElement script =
document.createElement('script') as DomHtmlScriptElement
..src = trustedUrl ?? _url
..async = true
..defer = true;
(target ?? document.head).appendChild(script);
return completer.future;
}
/// Exception thrown if the Trusted Types feature is supported, enabled, and it
/// has prevented this loader from injecting the JS SDK.
class TrustedTypesException implements Exception {
///
TrustedTypesException(this.message);
/// The message of the exception
final String message;
@override
String toString() => 'TrustedTypesException: $message';
}

View File

@ -0,0 +1,16 @@
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.0
environment:
sdk: ">=2.17.0 <3.0.0"
dependencies:
js: ^0.6.4
meta: ^1.3.0
dev_dependencies:
path: ^1.8.1
test: ^1.21.1

View File

@ -0,0 +1,3 @@
# Tests
Use `dart run tool/run_tests.dart` to run tests in this package.

View File

@ -0,0 +1,66 @@
// 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.
@TestOn('browser') // Uses package:js
import 'package:google_identity_services_web/loader.dart';
import 'package:google_identity_services_web/src/js_interop/dom.dart' as dom;
import 'package:google_identity_services_web/src/js_loader.dart';
import 'package:js/js_util.dart' as js_util;
import 'package:test/test.dart';
// NOTE: This file needs to be separated from the others because Content
// Security Policies can never be *relaxed* once set.
//
// In order to not introduce a dependency in the order of the tests, we split
// them in different files, depending on the strictness of their CSP:
//
// * js_loader_test.dart : default TT configuration (not enforced)
// * js_loader_tt_custom_test.dart : TT are customized, but allowed
// * js_loader_tt_forbidden_test.dart: TT are completely disallowed
void main() {
group('loadWebSdk (no TrustedTypes)', () {
final dom.DomHtmlElement target = dom.document.createElement('div');
test('Injects script into desired target', () async {
loadWebSdk(target: target);
// Target now should have a child that is a script element
final Object children = js_util.getProperty<Object>(target, 'children');
final Object injected = js_util.callMethod<Object>(
children,
'item',
<Object>[0],
);
expect(injected, isA<dom.DomHtmlScriptElement>());
final dom.DomHtmlScriptElement script =
injected as dom.DomHtmlScriptElement;
expect(js_util.getProperty<bool>(script, 'defer'), isTrue);
expect(js_util.getProperty<bool>(script, 'async'), isTrue);
expect(
js_util.getProperty<String>(script, 'src'),
'https://accounts.google.com/gsi/client',
);
});
test('Completes when the script loads', () async {
final Future<void> loadFuture = loadWebSdk(target: target);
Future<void>.delayed(const Duration(milliseconds: 100), () {
// Simulate the library calling `window.onGoogleLibraryLoad`.
js_util.callMethod<void>(
js_util.globalThis,
'onGoogleLibraryLoad',
<Object>[],
);
});
await expectLater(loadFuture, completes);
});
});
}

View File

@ -0,0 +1,47 @@
// 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.
@TestOn('browser') // Uses package:js
import 'package:google_identity_services_web/loader.dart';
import 'package:google_identity_services_web/src/js_interop/dom.dart' as dom;
import 'package:test/test.dart';
import 'tools.dart';
// NOTE: This file needs to be separated from the others because Content
// Security Policies can never be *relaxed* once set.
//
// In order to not introduce a dependency in the order of the tests, we split
// them in different files, depending on the strictness of their CSP:
//
// * js_loader_test.dart : default TT configuration (not enforced)
// * js_loader_tt_custom_test.dart : TT are customized, but allowed
// * js_loader_tt_forbidden_test.dart: TT are completely disallowed
void main() {
group('loadWebSdk (TrustedTypes configured)', () {
final dom.DomHtmlElement target = dom.document.createElement('div');
injectMetaTag(<String, String>{
'http-equiv': 'Content-Security-Policy',
'content': "trusted-types my-custom-policy-name 'allow-duplicates';",
});
test('Wrong policy name: Fail with TrustedTypesException', () {
expect(() {
loadWebSdk(target: target);
}, throwsA(isA<TrustedTypesException>()));
});
test('Correct policy name: Completes', () {
final Future<void> done = loadWebSdk(
target: target,
trustedTypePolicyName: 'my-custom-policy-name',
);
expect(done, isA<Future<void>>());
});
});
}

View File

@ -0,0 +1,38 @@
// 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.
@TestOn('browser') // Uses package:js
import 'package:google_identity_services_web/loader.dart';
import 'package:google_identity_services_web/src/js_interop/dom.dart' as dom;
import 'package:test/test.dart';
import 'tools.dart';
// NOTE: This file needs to be separated from the others because Content
// Security Policies can never be *relaxed* once set.
//
// In order to not introduce a dependency in the order of the tests, we split
// them in different files, depending on the strictness of their CSP:
//
// * js_loader_test.dart : default TT configuration (not enforced)
// * js_loader_tt_custom_test.dart : TT are customized, but allowed
// * js_loader_tt_forbidden_test.dart: TT are completely disallowed
void main() {
group('loadWebSdk (TrustedTypes forbidden)', () {
final dom.DomHtmlElement target = dom.document.createElement('div');
injectMetaTag(<String, String>{
'http-equiv': 'Content-Security-Policy',
'content': "trusted-types 'none';",
});
test('Fail with TrustedTypesException', () {
expect(() {
loadWebSdk(target: target);
}, throwsA(isA<TrustedTypesException>()));
});
});
}

View File

@ -0,0 +1,18 @@
// 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.
// ignore_for_file: avoid_print
@TestOn('vm')
import 'package:test/test.dart';
void main() {
test('Tell the user where to find the real tests', () {
print('---');
print('This package uses `dart test -p chrome` for its tests.');
print('See `README.md` for more info.');
print('---');
});
}

View File

@ -0,0 +1,19 @@
// 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 'package:google_identity_services_web/src/js_interop/dom.dart' as dom;
import 'package:js/js_util.dart' as js_util;
/// Injects a `<meta>` tag with the provided [attributes] into the [dom.document].
void injectMetaTag(Map<String, String> attributes) {
final dom.DomHtmlElement meta = dom.document.createElement('meta');
for (final MapEntry<String, String> attribute in attributes.entries) {
js_util.callMethod(
meta,
'setAttribute',
<String>[attribute.key, attribute.value],
);
}
dom.document.head.appendChild(meta);
}

View File

@ -0,0 +1,49 @@
// 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.
// Runs `dart test -p chrome` in the root of the google_identity_services_web package.
//
// Called from the custom-tests CI action.
//
// usage: dart run tool/run_tests.dart
// (needs a `chrome` executable in $PATH, or a tweak to dart_test.yaml)
import 'dart:io';
import 'package:path/path.dart' as p;
Future<void> main(List<String> args) async {
final Directory workingDir =
Directory(p.dirname(Platform.script.path)).parent;
final int status = await _runProcess(
'dart',
<String>[
'test',
'-p',
'chrome',
],
workingDirectory: workingDir.path,
);
exit(status);
}
Future<Process> _streamOutput(Future<Process> processFuture) async {
final Process process = await processFuture;
stdout.addStream(process.stdout);
stderr.addStream(process.stderr);
return process;
}
Future<int> _runProcess(
String command,
List<String> arguments, {
String? workingDirectory,
}) async {
final Process process = await _streamOutput(Process.start(
command,
arguments,
workingDirectory: workingDirectory,
));
return process.exitCode;
}

View File

@ -0,0 +1,9 @@
# Packages that only support `custom-test` in linux, because that's
# the only place where we install Chrome.
#
# This file is used to "--exclude" these packages from the relevant
# mac/windows CI test runs.
- cross_file
- google_identity_services_web