oauth2.js revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @fileoverview 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * OAuth2 class that handles retrieval/storage of an OAuth2 token. 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Uses a content script to trampoline the OAuth redirect page back into the 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * extension context. This works around the lack of native support for 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * chrome-extensions in OAuth2. 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(jamiewalch): Delete this code once Chromoting is a v2 app and uses the 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// identity API (http://crbug.com/ 134213). 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)'use strict'; 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @suppress {duplicate} */ 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var remoting = remoting || {}; 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @type {remoting.OAuth2} */ 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.oauth2 = null; 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @constructor */ 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2 = function() { 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Constants representing keys used for storing persistent state. 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */ 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.KEY_REFRESH_TOKEN_ = 'oauth2-refresh-token'; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */ 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.KEY_REFRESH_TOKEN_REVOKABLE_ = 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'oauth2-refresh-token-revokable'; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */ 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.KEY_ACCESS_TOKEN_ = 'oauth2-access-token'; 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */ 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.KEY_XSRF_TOKEN_ = 'oauth2-xsrf-token'; 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private */ 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.KEY_EMAIL_ = 'remoting-email'; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Constants for parameters used in retrieving the OAuth2 credentials. 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */ 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.SCOPE_ = 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'https://www.googleapis.com/auth/chromoting ' + 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'https://www.googleapis.com/auth/googletalk ' + 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'https://www.googleapis.com/auth/userinfo#email'; 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Configurable URLs/strings. 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} OAuth2 redirect URI. 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getRedirectUri_ = function() { 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_REDIRECT_URL; 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} API client ID. 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getClientId_ = function() { 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_CLIENT_ID; 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} API client secret. 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getClientSecret_ = function() { 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_CLIENT_SECRET; 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} OAuth2 authentication URL. 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getOAuth2AuthEndpoint_ = function() { 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_BASE_URL + '/auth'; 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} OAuth2 token URL. 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getOAuth2TokenEndpoint_ = function() { 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_BASE_URL + '/token'; 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} OAuth token revocation URL. 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getOAuth2RevokeTokenEndpoint_ = function() { 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_BASE_URL + '/revoke'; 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** @private 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {string} OAuth2 userinfo API URL. 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.getOAuth2ApiUserInfoEndpoint_ = function() { 972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return remoting.settings.OAUTH2_API_BASE_URL + '/v1/userinfo'; 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @return {boolean} True if the app is already authenticated. */ 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.isAuthenticated = function() { 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.getRefreshToken_()) { 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Removes all storage, and effectively unauthenticates the user. 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.clear = function() { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.removeItem(this.KEY_EMAIL_); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.clearAccessToken_(); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.clearRefreshToken_(); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Sets the refresh token. 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This method also marks the token as revokable, so that this object will 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * revoke the token when it no longer needs it. 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} token The new refresh token. 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.setRefreshToken = function(token) { 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.setItem(this.KEY_REFRESH_TOKEN_, escape(token)); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.setItem(this.KEY_REFRESH_TOKEN_REVOKABLE_, true); 1317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) window.localStorage.removeItem(this.KEY_EMAIL_); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.clearAccessToken_(); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Gets the refresh token. 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This method also marks the refresh token as not revokable, so that this 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * object will not revoke the token when it no longer needs it. After this 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * object has exported the token, it cannot know whether it is still in use 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * when this object no longer needs it. 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {?string} The refresh token, if authenticated, or NULL. 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.exportRefreshToken = function() { 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_REVOKABLE_); 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return this.getRefreshToken_(); 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {?string} The refresh token, if authenticated, or NULL. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.getRefreshToken_ = function() { 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var value = window.localStorage.getItem(this.KEY_REFRESH_TOKEN_); 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeof value == 'string') { 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return unescape(value); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Clears the refresh token. 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.clearRefreshToken_ = function() { 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (window.localStorage.getItem(this.KEY_REFRESH_TOKEN_REVOKABLE_)) { 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.revokeToken_(this.getRefreshToken_()); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_REVOKABLE_); 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} token The new access token. 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} expiration Expiration time in milliseconds since epoch. 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.setAccessToken = function(token, expiration) { 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var access_token = {'token': token, 'expiration': expiration}; 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.setItem(this.KEY_ACCESS_TOKEN_, 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) JSON.stringify(access_token)); 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns the current access token, setting it to a invalid value if none 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * existed before. 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {{token: string, expiration: number}} The current access token, or 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * an invalid token if not authenticated. 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.getAccessTokenInternal_ = function() { 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!window.localStorage.getItem(this.KEY_ACCESS_TOKEN_)) { 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Always be able to return structured data. 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.setAccessToken('', 0); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var accessToken = window.localStorage.getItem(this.KEY_ACCESS_TOKEN_); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeof accessToken == 'string') { 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var result = jsonParseSafe(accessToken); 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result && 'token' in result && 'expiration' in result) { 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return /** @type {{token: string, expiration: number}} */ result; 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.log('Invalid access token stored.'); 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return {'token': '', 'expiration': 0}; 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns true if the access token is expired, or otherwise invalid. 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Will throw if !isAuthenticated(). 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if a new access token is needed. 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.needsNewAccessToken_ = function() { 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.isAuthenticated()) { 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw 'Not Authenticated.'; 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var access_token = this.getAccessTokenInternal_(); 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!access_token['token']) { 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (Date.now() > access_token['expiration']) { 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.clearAccessToken_ = function() { 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.localStorage.removeItem(this.KEY_ACCESS_TOKEN_); 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update state based on token response from the OAuth2 /token endpoint. 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(XMLHttpRequest, string): void} onDone Callback to invoke on 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * completion. 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {XMLHttpRequest} xhr The XHR object for this request. 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.processTokenResponse_ = function(onDone, xhr) { 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @type {string} */ 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var accessToken = ''; 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xhr.status == 200) { 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try { 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't use jsonParseSafe here unless you move the definition out of 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // remoting.js, otherwise this won't work from the OAuth trampoline. 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(jamiewalch): Fix this once we're no longer using the trampoline. 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var tokens = JSON.parse(xhr.responseText); 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ('refresh_token' in tokens) { 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.setRefreshToken(tokens['refresh_token']); 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Offset by 120 seconds so that we can guarantee that the token 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we return will be valid for at least 2 minutes. 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the access token is to be useful, this object must make some 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // guarantee as to how long the token will be valid for. 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The choice of 2 minutes is arbitrary, but that length of time 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is part of the contract satisfied by callWithToken(). 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Offset by a further 30 seconds to account for RTT issues. 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) accessToken = /** @type {string} */ (tokens['access_token']); 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.setAccessToken(accessToken, 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (tokens['expires_in'] - (120 + 30)) * 1000 + Date.now()); 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } catch (err) { 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.error('Invalid "token" response from server:', 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @type {*} */ (err)); 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.error('Failed to get tokens. Status: ' + xhr.status + 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ' response: ' + xhr.responseText); 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onDone(xhr, accessToken); 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Asynchronously retrieves a new access token from the server. 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Will throw if !isAuthenticated(). 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(XMLHttpRequest): void} onDone Callback to invoke on 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * completion. 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.refreshAccessToken_ = function(onDone) { 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.isAuthenticated()) { 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw 'Not Authenticated.'; 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var parameters = { 3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'client_id': this.getClientId_(), 3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'client_secret': this.getClientSecret_(), 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'refresh_token': this.getRefreshToken_(), 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'grant_type': 'refresh_token' 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) remoting.xhr.post(this.getOAuth2TokenEndpoint_(), 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.processTokenResponse_.bind(this, onDone), 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parameters); 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Redirect page to get a new OAuth2 Refresh Token. 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.doAuthRedirect = function() { 317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) var xsrf_token = remoting.generateXsrfToken(); 3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) window.localStorage.setItem(this.KEY_XSRF_TOKEN_, xsrf_token); 3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var GET_CODE_URL = this.getOAuth2AuthEndpoint_() + '?' + 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) remoting.xhr.urlencodeParamHash({ 3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'client_id': this.getClientId_(), 3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'redirect_uri': this.getRedirectUri_(), 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'scope': this.SCOPE_, 3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'state': xsrf_token, 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'response_type': 'code', 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'access_type': 'offline', 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'approval_prompt': 'force' 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }); 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window.location.replace(GET_CODE_URL); 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Asynchronously exchanges an authorization code for a refresh token. 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} code The new refresh token. 3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string} state The state parameter received from the OAuth redirect. 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(XMLHttpRequest):void} onDone Callback to invoke on 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * completion. 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)remoting.OAuth2.prototype.exchangeCodeForToken = function(code, state, onDone) { 3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var xsrf_token = window.localStorage.getItem(this.KEY_XSRF_TOKEN_); 3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) window.localStorage.removeItem(this.KEY_XSRF_TOKEN_); 3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (xsrf_token == undefined || state != xsrf_token) { 3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Invalid XSRF token, or unexpected OAuth2 redirect. Abort. 3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) onDone(null); 3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var parameters = { 3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'client_id': this.getClientId_(), 3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'client_secret': this.getClientSecret_(), 3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'redirect_uri': this.getRedirectUri_(), 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'code': code, 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'grant_type': 'authorization_code' 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) remoting.xhr.post(this.getOAuth2TokenEndpoint_(), 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.processTokenResponse_.bind(this, onDone), 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parameters); 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Interprets unexpected HTTP response codes to authentication XMLHttpRequests. 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The caller should handle the usual expected responses (200, 400) separately. 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} xhrStatus Status (HTTP response code) of the XMLHttpRequest. 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {remoting.Error} An error code to be raised. 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.interpretUnexpectedXhrStatus_ = function(xhrStatus) { 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Return AUTHENTICATION_FAILED by default, so that the user can try to 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // recover from an unexpected failure by signing in again. 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @type {remoting.Error} */ 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var error = remoting.Error.AUTHENTICATION_FAILED; 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xhrStatus == 502 || xhrStatus == 503) { 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = remoting.Error.SERVICE_UNAVAILABLE; 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (xhrStatus == 0) { 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = remoting.Error.NETWORK_FAILURE; 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.warn('Unexpected authentication response code: ' + xhrStatus); 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return error; 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Revokes a refresh or an access token. 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string?} token An access or refresh token. 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.revokeToken_ = function(token) { 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!token || (token.length == 0)) { 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var parameters = { 'token': token }; 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @param {XMLHttpRequest} xhr The XHR reply. */ 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var processResponse = function(xhr) { 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xhr.status != 200) { 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.log('Failed to revoke token. Status: ' + xhr.status + 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ' ; response: ' + xhr.responseText + ' ; xhr: ', xhr); 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) remoting.xhr.post(this.getOAuth2RevokeTokenEndpoint_(), 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) processResponse, 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parameters); 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Call a function with an access token, refreshing it first if necessary. 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The access token will remain valid for at least 2 minutes. 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(string):void} onOk Function to invoke with access token if 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * an access token was successfully retrieved. 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(remoting.Error):void} onError Function to invoke with an 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * error code on failure. 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.callWithToken = function(onOk, onError) { 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.isAuthenticated()) { 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.needsNewAccessToken_()) { 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.refreshAccessToken_(this.onRefreshToken_.bind(this, onOk, onError)); 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onOk(this.getAccessTokenInternal_()['token']); 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onError(remoting.Error.NOT_AUTHENTICATED); 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Process token refresh results and notify caller. 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(string):void} onOk Function to invoke with access token if 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * an access token was successfully retrieved. 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(remoting.Error):void} onError Function to invoke with an 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * error code on failure. 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {XMLHttpRequest} xhr The result of the refresh operation. 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} accessToken The fresh access token. 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.onRefreshToken_ = function(onOk, onError, xhr, 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) accessToken) { 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @type {remoting.Error} */ 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var error = remoting.Error.UNEXPECTED; 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xhr.status == 200) { 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onOk(accessToken); 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (xhr.status == 400) { 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var result = 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @type {{error: string}} */ (jsonParseSafe(xhr.responseText)); 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result && result.error == 'invalid_grant') { 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = remoting.Error.AUTHENTICATION_FAILED; 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = this.interpretUnexpectedXhrStatus_(xhr.status); 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onError(error); 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Get the user's email address. 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(string):void} onOk Callback invoked when the email 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * address is available. 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(remoting.Error):void} onError Callback invoked if an 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * error occurs. 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {void} Nothing. 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.getEmail = function(onOk, onError) { 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var cached = window.localStorage.getItem(this.KEY_EMAIL_); 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeof cached == 'string') { 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onOk(cached); 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @type {remoting.OAuth2} */ 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var that = this; 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @param {XMLHttpRequest} xhr The XHR response. */ 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var onResponse = function(xhr) { 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var email = null; 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xhr.status == 200) { 4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var result = jsonParseSafe(xhr.responseText); 4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (result && 'email' in result) { 4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) window.localStorage.setItem(that.KEY_EMAIL_, result['email']); 4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) onOk(result['email']); 4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else { 4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) console.error( 4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'Cannot parse userinfo response: ', xhr.responseText, xhr); 4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) onError(remoting.Error.UNEXPECTED); 4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) console.error('Unable to get email address:', xhr.status, xhr); 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xhr.status == 401) { 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onError(remoting.Error.AUTHENTICATION_FAILED); 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onError(that.interpretUnexpectedXhrStatus_(xhr.status)); 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /** @param {string} token The access token. */ 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var getEmailFromToken = function(token) { 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var headers = { 'Authorization': 'OAuth ' + token }; 5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) remoting.xhr.get(that.getOAuth2ApiUserInfoEndpoint_(), 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) onResponse, '', headers); 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.callWithToken(getEmailFromToken, onError); 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * If the user's email address is cached, return it, otherwise return null. 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {?string} The email address, if it has been cached by a previous call 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to getEmail, otherwise null. 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)remoting.OAuth2.prototype.getCachedEmail = function() { 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var value = window.localStorage.getItem(this.KEY_EMAIL_); 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeof value == 'string') { 5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return value; 5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return null; 5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 524