Skip to content

Commit

Permalink
[gis_web] Adds id.renderButton JS-interop. (#3011)
Browse files Browse the repository at this point in the history
* [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.
  • Loading branch information
ditman committed Jan 10, 2023
1 parent b4bce7d commit b6fe67e
Show file tree
Hide file tree
Showing 15 changed files with 916 additions and 455 deletions.
8 changes: 8 additions & 0 deletions packages/google_identity_services_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.2.0

* Adds `renderButton` API to `id.dart`.
* **Breaking Change:** Makes JS-interop API more `dart2wasm`-friendly.
* Removes external getters for function types
* Introduces an external getter for the whole libraries instead.
* Updates `README.md` with the new way of `import`ing the desired libraries.

## 0.1.1

* Add optional `scope` to `OverridableTokenClientConfig` object.
Expand Down
26 changes: 23 additions & 3 deletions packages/google_identity_services_web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,29 @@ behind a [conditional import/export](https://dart.dev/guides/libraries/create-li

Once the SDK has been loaded, it can be used by importing the correct library:

* `import 'package:google_identity_services/id.dart' as id;` for Authentication
* `import 'package:google_identity_services/oauth2.dart' as oauth2;` for
Authorization.
* `import 'package:google_identity_services/id.dart';` for Authentication.
* This will expose an `id` JSObject that binds to `google.accounts.id`.
* `import 'package:google_identity_services/oauth2.dart';` for Authorization.
* This will expose an `oauth2` JSObject that binds to `google.accounts.oauth2`.

### Troubleshooting

Watch the browser's development tools JS console while using this package.
Information about errors during initialization and use of the library will be
displayed there.

Some common issues identified so far:

#### The given origin is not allowed for the given client ID

> When you perform local tests or development, **you must add both**
> `http://localhost` and `http://localhost:<port_number>` to the
> **Authorized JavaScript origins** box.
> The [Referrer-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy)
> response header must also be set to `no-referrer-when-downgrade` when using
> http and localhost.
* Read more: [Sign In with Google for Web - Setup - Get your Google API client ID](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid#get_your_google_api_client_id).

## Browser compatibility

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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);
});
});
}
Original file line number Diff line number Diff line change
@@ -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 comments on commit b6fe67e

Please sign in to comment.