Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/google_sign_in/google_sign_in_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.8.0

* Flutter for web initial release
26 changes: 26 additions & 0 deletions packages/google_sign_in/google_sign_in_web/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Copyright 2016, the Flutter project 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.
80 changes: 80 additions & 0 deletions packages/google_sign_in/google_sign_in_web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# google_sign_in_web

The web implementation of [google_sign_in](https://pub.dev/google_sign_in/google_sign_in)

## Usage

### Import the package
To use this plugin, follow the [plugin installation instructions](https://pub.dartlang.org/packages/google_sign_in#pub-pkg-tab-installing).

Remember that for web plugins you need to depend both on the "native" version that provides the Dart interface that you'll use in your app), and the "web" version, that provides the implementation of the plugin for the web platform.

This is what the above means to your `pubspec.yaml`:

```
...
dependencies:
...
google_sign_in: ^4.0.14
google_sign_in_web: ^0.8.0
...
...
```

### Web integration

First, go through the instructions [here](https://developers.google.com/identity/sign-in/web/sign-in#before_you_begin) to create your Google Sign-In OAuth client ID.

On your `web/index.html` file, add the following `meta` tag, somewhere in the
`head` of the document:

```
<meta name="google-signin-client_id" content="YOUR_GOOGLE_SIGN_IN_OAUTH_CLIENT_ID.apps.googleusercontent.com">
```

Read the rest of the instructions if you need to add extra APIs (like Google People API).


### Use the plugin
Add the following import to your Dart code:

```dart
import 'package:google_sign_in/google_sign_in.dart';
```

Initialize GoogleSignIn with the scopes you want:

```dart
GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: [
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);
```
[Full list of available scopes](https://developers.google.com/identity/protocols/googlescopes).

You can now use the `GoogleSignIn` class to authenticate in your Dart code, e.g.

```dart
Future<void> _handleSignIn() async {
try {
await _googleSignIn.signIn();
} catch (error) {
print(error);
}
}
```

## Example

Find the example wiring in the [Google sign-in example application](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in/example/lib/main.dart).

## API details

See the [google_sign_in.dart](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart) for more API details.

## Issues and feedback

Please file [issues](https://github.com/flutter/flutter/issues/new)
to send feedback or report a bug. Thank you!
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Exclude auto-generated files from analysis/linting
analyzer:
exclude:
- "lib/src/generated/**"

151 changes: 151 additions & 0 deletions packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2019 The Chromium 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 'dart:html' as html;

import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:js/js.dart';
import 'package:meta/meta.dart';

import 'src/generated/gapiauth2.dart' as auth2;
// TODO: Remove once this lands https://github.com/dart-lang/language/issues/671
import 'src/generated/gapiauth2.dart' show GoogleAuthExtensions;
import 'src/load_gapi.dart' as gapi;
import 'src/utils.dart' show gapiUserToPluginUserData;

const String _kClientIdMetaSelector = 'meta[name=google-signin-client_id]';
const String _kClientIdAttributeName = 'content';

@visibleForTesting
String gapiUrl = 'https://apis.google.com/js/platform.js';

/// Implementation of the google_sign_in plugin for Web
class GoogleSignInPlugin extends GoogleSignInPlatform {
GoogleSignInPlugin() {
_autoDetectedClientId = html
.querySelector(_kClientIdMetaSelector)
?.getAttribute(_kClientIdAttributeName);

_isGapiInitialized = gapi.inject(gapiUrl).then((_) => gapi.init());
}

Future<void> _isGapiInitialized;

@visibleForTesting
Future<void> get initialized => _isGapiInitialized;

String _autoDetectedClientId;
FutureOr<auth2.GoogleUser> _lastSeenUser;

static void registerWith(Registrar registrar) {
GoogleSignInPlatform.instance = GoogleSignInPlugin();
}

@override
Future<void> init(
{@required String hostedDomain,
List<String> scopes = const <String>[],
SignInOption signInOption = SignInOption.standard,
String clientId}) async {
final String appClientId = clientId ?? _autoDetectedClientId;
assert(
appClientId != null,
'ClientID not set. Either set it on a '
'<meta name="google-signin-client_id" content="CLIENT_ID" /> tag,'
' or pass clientId when calling init()');

assert(
!scopes.any((String scope) => scope.contains(' ')),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this assertion should happen in the app-facing package? Aren't spaces a problem in the mobile version of the plugin as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're a problem here because we use the space as a "join" character to pass the scopes to JS. In the mobile versions, the List is preserved all the way through.

I'm guessing a scope name with a space character is invalid there as well (scopes are URLs, after all), but I couldn't find any actual validation performed on the Android or IOS side. It will just probably... not work, or you'll get a bad request error or similar when authenticating.

This was added here to be helpful with the user.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(No validation on the app facing interface either)

'OAuth 2.0 Scopes for Google APIs can\'t contain spaces.'
'Check https://developers.google.com/identity/protocols/googlescopes '
'for a list of valid OAuth 2.0 scopes.');

await initialized;

final auth2.GoogleAuth auth = auth2.init(auth2.ClientConfig(
hosted_domain: hostedDomain,
// The js lib wants a space-separated list of values
scope: scopes.join(' '),
client_id: appClientId,
));

// Subscribe to changes in the auth instance returned by init,
// and cache the _lastSeenUser as we get notified of new values.
final Completer<auth2.GoogleUser> initUserCompleter =
Completer<auth2.GoogleUser>();

auth.currentUser.listen(allowInterop((auth2.GoogleUser nextUser) {
if (!initUserCompleter.isCompleted) {
initUserCompleter.complete(nextUser);
} else {
_lastSeenUser = nextUser;
}
}));
_lastSeenUser = initUserCompleter.future;

return null;
}

@override
Future<GoogleSignInUserData> signInSilently() async {
await initialized;

return gapiUserToPluginUserData(await _lastSeenUser);
}

@override
Future<GoogleSignInUserData> signIn() async {
await initialized;

return gapiUserToPluginUserData(await auth2.getAuthInstance().signIn());
}

@override
Future<GoogleSignInTokenData> getTokens(
{@required String email, bool shouldRecoverAuth}) async {
await initialized;

final auth2.GoogleUser currentUser =
auth2.getAuthInstance()?.currentUser?.get();
final auth2.AuthResponse response = currentUser.getAuthResponse();

return GoogleSignInTokenData(
idToken: response.id_token, accessToken: response.access_token);
}

@override
Future<void> signOut() async {
await initialized;

return auth2.getAuthInstance().signOut();
}

@override
Future<void> disconnect() async {
await initialized;

final auth2.GoogleUser currentUser =
auth2.getAuthInstance()?.currentUser?.get();
return currentUser.disconnect();
}

@override
Future<bool> isSignedIn() async {
await initialized;

final auth2.GoogleUser currentUser =
auth2.getAuthInstance()?.currentUser?.get();
return currentUser.isSignedIn();
}

@override
Future<void> clearAuthCache({String token}) async {
await initialized;

_lastSeenUser = null;
return auth2.getAuthInstance().disconnect();
}
}
Loading