diff options
Diffstat (limited to 'chromium/third_party/catapult/third_party/polymer2/bower_components/google-signin/google-signin-aware.html')
-rw-r--r-- | chromium/third_party/catapult/third_party/polymer2/bower_components/google-signin/google-signin-aware.html | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/chromium/third_party/catapult/third_party/polymer2/bower_components/google-signin/google-signin-aware.html b/chromium/third_party/catapult/third_party/polymer2/bower_components/google-signin/google-signin-aware.html new file mode 100644 index 00000000000..e6e731bfad1 --- /dev/null +++ b/chromium/third_party/catapult/third_party/polymer2/bower_components/google-signin/google-signin-aware.html @@ -0,0 +1,824 @@ +<!-- +Copyright 2014 Google Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<link rel="import" href="../polymer/polymer.html"> +<link rel="import" href="../google-apis/google-js-api.html"> + +<script> + (function() { + + /** + * Enum of attributes to be passed through to the login API call. + * @readonly + * @enum {string} + */ + var ProxyLoginAttributes = { + 'appPackageName': 'apppackagename', + 'clientId': 'clientid', + 'cookiePolicy': 'cookiepolicy', + 'hostedDomain': 'hostedDomain', + 'openidPrompt': 'prompt', + 'requestVisibleActions': 'requestvisibleactions' + }; + + /** + * AuthEngine does all interactions with gapi.auth2 + * + * It is tightly coupled with <google-signin-aware> element + * The elements configure AuthEngine. + * AuthEngine propagates all authentication events to all google-signin-aware elements + * + * API used: https://developers.google.com/identity/sign-in/web/reference + * + */ + var AuthEngine = { + + /** + * oauth2 argument, set by google-signin-aware + */ + _clientId: null, + + get clientId() { + return this._clientId; + }, + + set clientId(val) { + if (this._clientId && val && val != this._clientId) { + throw new Error('clientId cannot change. Values do not match. New: ' + val + ' Old:' + this._clientId); + } + if (val && val != this._clientId) { + this._clientId = val; + this.initAuth2(); + } + }, + + /** + * oauth2 argument, set by google-signin-aware + */ + _cookiePolicy: 'single_host_origin', + + get cookiePolicy() { + return this._cookiePolicy; + }, + + set cookiePolicy(val) { + if (val) { + this._cookiePolicy = val; + } + }, + + /** + * oauth2 argument, set by google-signin-aware + */ + _appPackageName: '', + + get appPackageName() { + return this._appPackageName; + }, + + set appPackageName(val) { + if (this._appPackageName && val && val != this._appPackageName) { + throw new Error('appPackageName cannot change. Values do not match. New: ' + val + ' Old: ' + this._appPackageName); + } + if (val) { + this._appPackageName = val; + } + }, + + /** + * oauth2 argument, set by google-signin-aware + */ + _requestVisibleActions: '', + + get requestVisibleactions() { + return this._requestVisibleActions; + }, + + set requestVisibleactions(val) { + if (this._requestVisibleActions && val && val != this._requestVisibleActions) { + throw new Error('requestVisibleactions cannot change. Values do not match. New: ' + val + ' Old: ' + this._requestVisibleActions); + } + if (val) + this._requestVisibleActions = val; + }, + + /** + * oauth2 argument, set by google-signin-aware + */ + _hostedDomain: '', + + get hostedDomain() { + return this._hostedDomain; + }, + + set hostedDomain(val) { + if (this._hostedDomain && val && val != this._hostedDomain) { + throw new Error('hostedDomain cannot change. Values do not match. New: ' + val + ' Old: ' + this._hostedDomain); + } + if (val) + this._hostedDomain = val; + }, + + /** + * oauth2 argument, set by google-signin-aware + */ + _openidPrompt: '', + + get openidPrompt() { + return this._openidPrompt; + }, + + set openidPrompt(val) { + if (typeof val !== 'string') { + throw new Error( + 'openidPrompt must be a string. Received ' + typeof val); + } + if (val) { + var values = val.split(' '); + values = values.map(function(v) { + return v.trim(); + }); + values = values.filter(function(v) { + return v; + }); + var validValues = {none: 0, login: 0, consent: 0, select_account: 0}; + values.forEach(function(v) { + if (v == 'none' && values.length > 1) { + throw new Error( + 'none cannot be combined with other openidPrompt values'); + } + if (!(v in validValues)) { + throw new Error( + 'invalid openidPrompt value ' + v + + '. Valid values: ' + Object.keys(validValues).join(', ')); + } + }); + } + this._openidPrompt = val; + }, + + /** Is offline access currently enabled in the google-signin-aware element? */ + _offline: false, + + get offline() { + return this._offline; + }, + + set offline(val) { + this._offline = val; + this.updateAdditionalAuth(); + }, + + /** Should we force a re-prompt for offline access? */ + _offlineAlwaysPrompt: false, + + get offlineAlwaysPrompt() { + return this._offlineAlwaysPrompt; + }, + + set offlineAlwaysPrompt(val) { + this._offlineAlwaysPrompt = val; + this.updateAdditionalAuth(); + }, + + /** Have we already gotten offline access from Google during this session? */ + offlineGranted: false, + + /** <google-js-api> */ + _apiLoader: null, + + /** an array of wanted scopes. oauth2 argument */ + _requestedScopeArray: [], + + /** _requestedScopeArray as string */ + get requestedScopes() { + return this._requestedScopeArray.join(' '); + }, + + /** Is auth library initalized? */ + _initialized: false, + + /** Is user signed in? */ + _signedIn: false, + + /** Currently granted scopes */ + _grantedScopeArray: [], + + /** True if additional authorization is required */ + _needAdditionalAuth: true, + + /** True if have google+ scopes */ + _hasPlusScopes: false, + + /** + * array of <google-signin-aware> + * state changes are broadcast to them + */ + signinAwares: [], + + init: function() { + this._apiLoader = document.createElement('google-js-api'); + this._apiLoader.addEventListener('js-api-load', this.loadAuth2.bind(this)); + if (Polymer.Element) { + document.body.appendChild(this._apiLoader); + } + }, + + loadAuth2: function() { + gapi.load('auth2', this.initAuth2.bind(this)); + }, + + initAuth2: function() { + if (!('gapi' in window) || !('auth2' in window.gapi) || !this.clientId) { + return; + } + var auth = gapi.auth2.init({ + 'client_id': this.clientId, + 'cookie_policy': this.cookiePolicy, + 'scope': this.requestedScopes, + 'hosted_domain': this.hostedDomain + }); + + auth['currentUser'].listen(this.handleUserUpdate.bind(this)); + + auth.then( + function onFulfilled() { + // Let the current user listener trigger the changes. + }, + function onRejected(error) { + console.error(error); + } + ); + }, + + handleUserUpdate: function(newPrimaryUser) { + // update and broadcast currentUser + var isSignedIn = newPrimaryUser.isSignedIn(); + if (isSignedIn != this._signedIn) { + this._signedIn = isSignedIn; + for (var i=0; i<this.signinAwares.length; i++) { + this.signinAwares[i]._setSignedIn(isSignedIn); + } + } + // update and broadcast initialized property the first time the isSignedIn property is set. + if(!this._initialized) { + for (var i=0; i<this.signinAwares.length; i++) { + this.signinAwares[i]._setInitialized(true); + } + this._initialized = true; + } + + + // update granted scopes + this._grantedScopeArray = this.strToScopeArray( + newPrimaryUser.getGrantedScopes()); + // console.log(this._grantedScopeArray); + this.updateAdditionalAuth(); + + var response = newPrimaryUser.getAuthResponse(); + for (var i=0; i<this.signinAwares.length; i++) { + this.signinAwares[i]._updateScopeStatus(response); + } + }, + + setOfflineCode: function(code) { + for (var i=0; i<this.signinAwares.length; i++) { + this.signinAwares[i]._updateOfflineCode(code); + } + }, + + /** convert scope string to scope array */ + strToScopeArray: function(str) { + if (!str) { + return []; + } + // remove extra spaces, then split + var scopes = str.replace(/\ +/g, ' ').trim().split(' '); + for (var i=0; i<scopes.length; i++) { + scopes[i] = scopes[i].toLowerCase(); + // Handle scopes that will be deprecated but are still returned with their old value + if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.profile') { + scopes[i] = 'profile'; + } + if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.email') { + scopes[i] = 'email'; + } + } + // return with duplicates filtered out + return scopes.filter( function(value, index, self) { + return self.indexOf(value) === index; + }); + }, + + /** true if scopes have google+ scopes */ + isPlusScope: function(scope) { + return (scope.indexOf('/auth/games') > -1) + || (scope.indexOf('auth/plus.') > -1 && scope.indexOf('auth/plus.me') < 0); + }, + + /** true if scopes have been granted */ + hasGrantedScopes: function(scopeStr) { + var scopes = this.strToScopeArray(scopeStr); + for (var i=0; i< scopes.length; i++) { + if (this._grantedScopeArray.indexOf(scopes[i]) === -1) + return false; + } + return true; + }, + + /** request additional scopes */ + requestScopes: function(newScopeStr) { + var newScopes = this.strToScopeArray(newScopeStr); + var scopesUpdated = false; + for (var i=0; i<newScopes.length; i++) { + if (this._requestedScopeArray.indexOf(newScopes[i]) === -1) { + this._requestedScopeArray.push(newScopes[i]); + scopesUpdated = true; + } + } + if (scopesUpdated) { + this.updateAdditionalAuth(); + this.updatePlusScopes(); + } + }, + + /** update status of _needAdditionalAuth */ + updateAdditionalAuth: function() { + var needMoreAuth = false; + if ((this.offlineAlwaysPrompt || this.offline ) && !this.offlineGranted) { + needMoreAuth = true; + } else { + for (var i=0; i<this._requestedScopeArray.length; i++) { + if (this._grantedScopeArray.indexOf(this._requestedScopeArray[i]) === -1) { + needMoreAuth = true; + break; + } + } + } + if (this._needAdditionalAuth != needMoreAuth) { + this._needAdditionalAuth = needMoreAuth; + // broadcast new value + for (var i=0; i<this.signinAwares.length; i++) { + this.signinAwares[i]._setNeedAdditionalAuth(needMoreAuth); + } + } + }, + + updatePlusScopes: function() { + var hasPlusScopes = false; + for (var i = 0; i < this._requestedScopeArray.length; i++) { + if (this.isPlusScope(this._requestedScopeArray[i])) { + hasPlusScopes = true; + break; + } + } + if (this._hasPlusScopes != hasPlusScopes) { + this._hasPlusScopes = hasPlusScopes; + for (var i=0; i<this.signinAwares.length; i++) { + this.signinAwares[i]._setHasPlusScopes(hasPlusScopes); + } + } + }, + /** + * attached <google-signin-aware> + * @param {!GoogleSigninAwareElement} aware element to add + */ + attachSigninAware: function(aware) { + if (this.signinAwares.indexOf(aware) == -1) { + this.signinAwares.push(aware); + // Initialize aware properties + aware._setNeedAdditionalAuth(this._needAdditionalAuth); + aware._setInitialized(this._initialized); + aware._setSignedIn(this._signedIn); + aware._setHasPlusScopes(this._hasPlusScopes); + } else { + console.warn('signinAware attached more than once', aware); + } + }, + + detachSigninAware: function(aware) { + var index = this.signinAwares.indexOf(aware); + if (index != -1) { + this.signinAwares.splice(index, 1); + } else { + console.warn('Trying to detach unattached signin-aware'); + } + }, + + /** returns scopes not granted */ + getMissingScopes: function() { + return this._requestedScopeArray.filter( function(scope) { + return this._grantedScopeArray.indexOf(scope) === -1; + }.bind(this)).join(' '); + }, + + assertAuthInitialized: function() { + if (!this.clientId) { + throw new Error("AuthEngine not initialized. clientId has not been configured."); + } + if (!('gapi' in window)) { + throw new Error("AuthEngine not initialized. gapi has not loaded."); + } + if (!('auth2' in window.gapi)) { + throw new Error("AuthEngine not initialized. auth2 not loaded."); + } + }, + + /** pops up sign-in dialog */ + signIn: function() { + this.assertAuthInitialized(); + var params = { + 'scope': this.getMissingScopes() + }; + + // Proxy specific attributes through to the signIn options. + Object.keys(ProxyLoginAttributes).forEach(function(key) { + if (this[key] && this[key] !== '') { + params[ProxyLoginAttributes[key]] = this[key]; + } + }, this); + + var promise; + var user = gapi.auth2.getAuthInstance()['currentUser'].get(); + if (!(this.offline || this.offlineAlwaysPrompt)) { + if (user.getGrantedScopes()) { + // additional auth, skip multiple account dialog + promise = user.grant(params); + } else { + // initial signin + promise = gapi.auth2.getAuthInstance().signIn(params); + } + } else { + params.redirect_uri = 'postmessage'; + if (this.offlineAlwaysPrompt) { + params.approval_prompt = 'force'; + } + + // Despite being documented at https://goo.gl/tiO0Bk + // It doesn't seem like user.grantOfflineAccess() actually exists in + // the current version of the Google Sign-In JS client we're using + // through GoogleWebComponents. So in the offline case, we will not + // distinguish between a first auth and an additional one. + promise = gapi.auth2.getAuthInstance().grantOfflineAccess(params); + } + promise.then( + function onFulfilled(response) { + // If login was offline, response contains one string "code" + // Otherwise it contains the user object already + var newUser; + if (response.code) { + AuthEngine.offlineGranted = true; + newUser = gapi.auth2.getAuthInstance()['currentUser'].get(); + AuthEngine.setOfflineCode(response.code); + } else { + newUser = response; + } + + var authResponse = newUser.getAuthResponse(); + // Let the current user listener trigger the changes. + }, + function onRejected(error) { + // Access denied is not an error, user hit cancel + if ("Access denied." !== error.reason) { + this.signinAwares.forEach(function(awareInstance) { + awareInstance.errorNotify(error); + }); + } + }.bind(this) + ); + }, + + /** signs user out */ + signOut: function() { + this.assertAuthInitialized(); + gapi.auth2.getAuthInstance().signOut().then( + function onFulfilled() { + // Let the current user listener trigger the changes. + }, + function onRejected(error) { + console.error(error); + } + ); + } + }; + + AuthEngine.init(); + +/** +`google-signin-aware` is used to enable authentication in custom elements by +interacting with a google-signin element that needs to be present somewhere +on the page. + +The `scopes` attribute allows you to specify which scope permissions are required +(e.g do you want to allow interaction with the Google Drive API). + +The `google-signin-aware-success` event is triggered when a user successfully +authenticates. If either `offline` or `offlineAlwaysPrompt` is set to true, successful +authentication will also trigger the `google-signin-offline-success`event. +The `google-signin-aware-signed-out` event is triggered when a user explicitly +signs out via the google-signin element. + +You can bind to `isAuthorized` property to monitor authorization state. +##### Example + + <google-signin-aware scopes="https://www.googleapis.com/auth/drive"></google-signin-aware> + + +##### Example with offline + <template id="awareness" is="dom-bind"> + <google-signin-aware + scopes="https://www.googleapis.com/auth/drive" + offline + on-google-signin-aware-success="handleSignin" + on-google-signin-offline-success="handleOffline"></google-signin-aware> + <\/template> + <script> + var aware = document.querySelector('#awareness'); + aware.handleSignin = function(response) { + var user = gapi.auth2.getAuthInstance()['currentUser'].get(); + console.log('User name: ' + user.getBasicProfile().getName()); + }; + aware.handleOffline = function(response) { + console.log('Offline code received: ' + response.detail.code); + // Here you would POST response.detail.code to your webserver, which can + // exchange the authorization code for an access token. More info at: + // https://developers.google.com/identity/protocols/OAuth2WebServer + }; + <\/script> +*/ + Polymer({ + + is: 'google-signin-aware', + + /** + * Fired when this scope has been authorized + * @param {Object} result Authorization result. + * @event google-signin-aware-success + */ + + /** + * Fired when an offline authorization is successful. + * @param {{code: string}} detail - + * code: The one-time authorization code from Google. + * Your application can exchange this for an `access_token` and `refresh_token` + * @event google-signin-offline-success + */ + + /** + * Fired when this scope is not authorized + * @event google-signin-aware-signed-out + */ + + /** + * Fired when there is an error during the signin flow. + * @param {Object} detail The error object returned from the OAuth 2 flow. + * @event google-signin-aware-error + */ + + /** + * This block is needed so the previous @param is not assigned to the next property. + */ + + properties: { + /** + * App package name for android over-the-air installs. + * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs) + */ + appPackageName: { + type: String, + observer: '_appPackageNameChanged' + }, + + /** + * a Google Developers clientId reference + */ + clientId: { + type: String, + observer: '_clientIdChanged' + }, + + /** + * The cookie policy defines what URIs have access to the session cookie + * remembering the user's sign-in state. + * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information. + * @default 'single_host_origin' + */ + cookiePolicy: { + type: String, + observer: '_cookiePolicyChanged' + }, + + /** + * The app activity types you want to write on behalf of the user + * (e.g http://schemas.google.com/AddActivity) + * + */ + requestVisibleActions: { + type: String, + observer: '_requestVisibleActionsChanged' + }, + + /** + * The Google Apps domain to which users must belong to sign in. + * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information. + */ + hostedDomain: { + type: String, + observer: '_hostedDomainChanged' + }, + + /** + * Allows for offline `access_token` retrieval during the signin process. + * See also `offlineAlwaysPrompt`. You only need to set one of the two; if both + * are set, the behavior of `offlineAlwaysPrompt` will override `offline`. + */ + offline: { + type: Boolean, + value: false, + observer: '_offlineChanged' + }, + + /** + * Works the same as `offline` with the addition that it will always + * force a re-prompt to the user, guaranteeing that you will get a + * refresh_token even if the user has already granted offline access to + * this application. You only need to set one of `offline` or + * `offlineAlwaysPrompt`, not both. + */ + offlineAlwaysPrompt: { + type: Boolean, + value: false, + observer: '_offlineAlwaysPromptChanged' + }, + + /** + * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive) + * and should be space-delimited. + */ + scopes: { + type: String, + value: 'profile', + observer: '_scopesChanged' + }, + + /** + * Space-delimited, case-sensitive list of strings that + * specifies whether the the user is prompted for reauthentication + * and/or consent. The defined values are: + * none: do not display authentication or consent pages. + * This value is mutually exclusive with the rest. + * login: always prompt the user for reauthentication. + * consent: always show consent screen. + * select_account: always show account selection page. + * This enables a user who has multiple accounts to select amongst + * the multiple accounts that they might have current sessions for. + * For more information, see "prompt" parameter description in + * https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters + */ + openidPrompt: { + type: String, + value: '', + observer: '_openidPromptChanged' + }, + + /** + * True when the auth library has been initialized, and signedIn property value is set from the first api response. + */ + initialized: { + type: Boolean, + notify: true, + readOnly: true + }, + + /** + * True if user is signed in + */ + signedIn: { + type: Boolean, + notify: true, + readOnly: true + }, + + /** + * True if authorizations for *this* element have been granted + */ + isAuthorized: { + type: Boolean, + notify: true, + readOnly: true, + value: false + }, + + /** + * True if additional authorizations for *any* element are required + */ + needAdditionalAuth: { + type: Boolean, + notify: true, + readOnly: true + }, + + /** + * True if *any* element has google+ scopes + */ + hasPlusScopes: { + type: Boolean, + value: false, + notify: true, + readOnly: true + } + }, + + attached: function() { + AuthEngine.attachSigninAware(this); + }, + + detached: function() { + AuthEngine.detachSigninAware(this); + }, + + /** pops up the authorization dialog */ + signIn: function() { + AuthEngine.signIn(); + }, + + /** signs user out */ + signOut: function() { + AuthEngine.signOut(); + }, + + errorNotify: function(error) { + this.fire('google-signin-aware-error', error); + }, + + _appPackageNameChanged: function(newName, oldName) { + AuthEngine.appPackageName = newName; + }, + + _clientIdChanged: function(newId, oldId) { + AuthEngine.clientId = newId; + }, + + _cookiePolicyChanged: function(newPolicy, oldPolicy) { + AuthEngine.cookiePolicy = newPolicy; + }, + + _requestVisibleActionsChanged: function(newVal, oldVal) { + AuthEngine.requestVisibleActions = newVal; + }, + + _hostedDomainChanged: function(newVal, oldVal) { + AuthEngine.hostedDomain = newVal; + }, + + _offlineChanged: function(newVal, oldVal) { + AuthEngine.offline = newVal; + }, + + _offlineAlwaysPromptChanged: function(newVal, oldVal) { + AuthEngine.offlineAlwaysPrompt = newVal; + }, + + _scopesChanged: function(newVal, oldVal) { + AuthEngine.requestScopes(newVal); + this._updateScopeStatus(undefined); + }, + + _openidPromptChanged: function(newVal, oldVal) { + AuthEngine.openidPrompt = newVal; + }, + + _updateScopeStatus: function(user) { + var newAuthorized = this.signedIn && AuthEngine.hasGrantedScopes(this.scopes); + if (newAuthorized !== this.isAuthorized) { + this._setIsAuthorized(newAuthorized); + if (newAuthorized) { + this.fire('google-signin-aware-success', user); + } + else { + this.fire('google-signin-aware-signed-out', user); + } + } + }, + + _updateOfflineCode: function(code) { + if (code) { + this.fire('google-signin-offline-success', {code: code}); + } + } + }); + })(); +</script> |