Skip to content

Commit 7781aee

Browse files
authored
[gis_web] Improves renderButton API exports. (flutter#3432)
[gis_web] Improves renderButton API exports.
1 parent a4b1ebb commit 7781aee

File tree

12 files changed

+119
-8
lines changed

12 files changed

+119
-8
lines changed

packages/google_identity_services_web/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.2.1
2+
3+
* Relaxes the `renderButton` API so any JS-Interop Object can be its `target`.
4+
* Exposes the `Button*` configuration enums, so the rendered button can be configured.
5+
16
## 0.2.0
27

38
* Adds `renderButton` API to `id.dart`.

packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:google_identity_services_web/id.dart';
99
import 'package:integration_test/integration_test.dart';
1010
import 'package:js/js.dart';
1111

12+
import 'src/dom.dart';
1213
import 'utils.dart' as utils;
1314

1415
void main() async {
@@ -19,6 +20,17 @@ void main() async {
1920
await utils.installGisMock();
2021
});
2122

23+
group('renderButton', () {
24+
testWidgets('supports a js-interop target from any library', (_) async {
25+
final DomElement target = createDomElement('div');
26+
27+
id.renderButton(target);
28+
29+
final DomElement? button = target.querySelector('button');
30+
expect(button, isNotNull);
31+
});
32+
});
33+
2234
group('prompt', () {
2335
testWidgets('supports a moment notification callback', (_) async {
2436
id.initialize(IdConfiguration(client_id: 'testing_1-2-3'));
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// ignore_for_file: public_member_api_docs
6+
7+
import 'package:js/js.dart';
8+
import 'package:js/js_util.dart' as js_util;
9+
10+
@JS()
11+
@staticInterop
12+
class DomDocument {}
13+
14+
extension DomDocumentExtension on DomDocument {
15+
DomElement createElement(String name, [Object? options]) =>
16+
js_util.callMethod(this, 'createElement',
17+
<Object>[name, if (options != null) options]) as DomElement;
18+
}
19+
20+
@JS()
21+
@staticInterop
22+
class DomElement {}
23+
24+
extension DomElementExtension on DomElement {
25+
external DomElement? querySelector(String selector);
26+
}
27+
28+
@JS('document')
29+
external DomDocument get domDocument;
30+
31+
DomElement createDomElement(String tag) => domDocument.createElement(tag);

packages/google_identity_services_web/example/lib/main.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import 'package:google_identity_services_web/id.dart';
99
import 'package:google_identity_services_web/loader.dart' as gis;
1010
// #enddocregion use-loader
1111
import 'package:js/js.dart' show allowInterop;
12-
import 'package:jwt_decoder/jwt_decoder.dart' as jwt;
12+
13+
import 'src/jwt.dart' as jwt;
1314

1415
// #docregion use-loader
1516
void main() async {
@@ -32,7 +33,7 @@ void main() async {
3233
/// Handles the ID token returned from the One Tap prompt.
3334
/// See: https://developers.google.com/identity/gsi/web/reference/js-reference#callback
3435
void onCredentialResponse(CredentialResponse o) {
35-
final Map<String, dynamic>? payload = jwt.JwtDecoder.tryDecode(o.credential!);
36+
final Map<String, dynamic>? payload = jwt.decodePayload(o.credential);
3637
if (payload != null) {
3738
print('Hello, ${payload["name"]}');
3839
print(o.select_by);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
/// A codec that can encode/decode JWT payloads.
8+
///
9+
/// See https://www.rfc-editor.org/rfc/rfc7519#section-3
10+
final Codec<Object?, String> _jwtCodec = json.fuse(utf8).fuse(base64);
11+
12+
/// A RegExp that can match, and extract parts from a JWT Token.
13+
///
14+
/// A JWT token consists of 3 base-64 encoded parts of data separated by periods:
15+
///
16+
/// header.payload.signature
17+
///
18+
/// More info: https://regexr.com/789qc
19+
final RegExp _jwtTokenRegexp = RegExp(
20+
r'^(?<header>[^\.\s]+)\.(?<payload>[^\.\s]+)\.(?<signature>[^\.\s]+)$');
21+
22+
/// Decodes the `claims` of a JWT token and returns them as a Map.
23+
///
24+
/// JWT `claims` are stored as a JSON object in the `payload` part of the token.
25+
///
26+
/// (This method does not validate the signature of the token.)
27+
///
28+
/// See https://www.rfc-editor.org/rfc/rfc7519#section-3
29+
Map<String, Object?>? decodePayload(String? token) {
30+
if (token != null) {
31+
final RegExpMatch? match = _jwtTokenRegexp.firstMatch(token);
32+
if (match != null) {
33+
return _decodeJwtPayload(match.namedGroup('payload'));
34+
}
35+
}
36+
37+
return null;
38+
}
39+
40+
/// Decodes a JWT payload using the [_jwtCodec].
41+
Map<String, Object?>? _decodeJwtPayload(String? payload) {
42+
try {
43+
// Payload must be normalized before passing it to the codec
44+
return _jwtCodec.decode(base64.normalize(payload!))
45+
as Map<String, Object?>?;
46+
} catch (_) {
47+
// Do nothing, we always return null for any failure.
48+
}
49+
return null;
50+
}

packages/google_identity_services_web/example/pubspec.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ dependencies:
1313
path: ../
1414
http: ^0.13.5
1515
js: ^0.6.4
16-
jwt_decoder: 2.0.1
1716

1817
dev_dependencies:
1918
build_runner: ^2.1.10 # To extract README excerpts only.

packages/google_identity_services_web/example/web/mock-gis.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ class Id {
3333
initialize(config) {
3434
this.config = config;
3535
}
36+
renderButton(target, config) {
37+
// Simulate rendering a button.
38+
target.replaceChildren();
39+
target.dataset.buttonConfig = config;
40+
let button = document.createElement('button');
41+
target.append(button);
42+
}
3643
prompt(momentListener) {
3744
callAsync(() => {
3845
if (this.mockCredentialResponse) {

packages/google_identity_services_web/lib/id.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
export 'src/js_interop/google_accounts_id.dart';
66
export 'src/js_interop/shared.dart'
77
show
8+
ButtonLogoAlignment,
9+
ButtonShape,
10+
ButtonSize,
11+
ButtonText,
12+
ButtonTheme,
13+
ButtonType,
814
CredentialSelectBy,
15+
GoogleIdentityServicesErrorType,
916
MomentDismissedReason,
1017
MomentNotDisplayedReason,
1118
MomentSkippedReason,

packages/google_identity_services_web/lib/oauth2.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
// found in the LICENSE file.
44

55
export 'src/js_interop/google_accounts_oauth2.dart';
6-
export 'src/js_interop/shared.dart' show UxMode;
6+
export 'src/js_interop/shared.dart'
7+
show GoogleIdentityServicesErrorType, UxMode;

packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ library google_accounts_id;
1414

1515
import 'package:js/js.dart';
1616

17-
import 'dom.dart';
1817
import 'shared.dart';
1918

2019
/// Binding to the `google.accounts.id` JS global.
@@ -93,7 +92,7 @@ extension GoogleAccountsIdExtension on GoogleAccountsId {
9392
/// Method: google.accounts.id.renderButton
9493
/// https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.renderButton
9594
external void renderButton(
96-
DomHtmlElement parent, [
95+
Object parent, [
9796
GsiButtonConfiguration options,
9897
]);
9998

0 commit comments

Comments
 (0)