Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview | 6 * @fileoverview |
| 7 * Wrapper class for Chrome's identity API. | 7 * Wrapper class for Chrome's identity API. |
| 8 */ | 8 */ |
| 9 /** @suppress {duplicate} */ | 9 /** @suppress {duplicate} */ |
| 10 var remoting = remoting || {}; | 10 var remoting = remoting || {}; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 * @param {remoting.Identity.ConsentDialog=} opt_consentDialog | 24 * @param {remoting.Identity.ConsentDialog=} opt_consentDialog |
| 25 * @constructor | 25 * @constructor |
| 26 */ | 26 */ |
| 27 remoting.Identity = function(opt_consentDialog) { | 27 remoting.Identity = function(opt_consentDialog) { |
| 28 /** @private */ | 28 /** @private */ |
| 29 this.consentDialog_ = opt_consentDialog; | 29 this.consentDialog_ = opt_consentDialog; |
| 30 /** @private {string} */ | 30 /** @private {string} */ |
| 31 this.email_ = ''; | 31 this.email_ = ''; |
| 32 /** @private {string} */ | 32 /** @private {string} */ |
| 33 this.fullName_ = ''; | 33 this.fullName_ = ''; |
| 34 /** @type {base.Deferred<string>} */ | 34 /** @private {Object<base.Deferred<string>>} */ |
| 35 this.authTokenDeferred_ = null; | 35 this.authTokensDeferred_ = {}; |
| 36 /** @private {boolean} */ | 36 /** @private {boolean} */ |
| 37 this.interactive_ = false; | 37 this.interactive_ = false; |
| 38 }; | 38 }; |
| 39 | 39 |
| 40 /** | 40 /** |
| 41 * chrome.identity.getAuthToken should be initiated from user interactions if | 41 * chrome.identity.getAuthToken should be initiated from user interactions if |
| 42 * called with interactive equals true. This interface prompts a dialog for | 42 * called with interactive equals true. This interface prompts a dialog for |
| 43 * the user's consent. | 43 * the user's consent. |
| 44 * | 44 * |
| 45 * @interface | 45 * @interface |
| 46 */ | 46 */ |
| 47 remoting.Identity.ConsentDialog = function() {}; | 47 remoting.Identity.ConsentDialog = function() {}; |
| 48 | 48 |
| 49 /** | 49 /** |
| 50 * @return {Promise} A Promise that resolves when permission to start an | 50 * @return {Promise} A Promise that resolves when permission to start an |
| 51 * interactive flow is granted. | 51 * interactive flow is granted. |
| 52 */ | 52 */ |
| 53 remoting.Identity.ConsentDialog.prototype.show = function() {}; | 53 remoting.Identity.ConsentDialog.prototype.show = function() {}; |
| 54 | 54 |
| 55 /** | 55 /** |
| 56 * Gets an access token. | 56 * Gets an access token. |
| 57 * | 57 * |
| 58 * @param {Array<string>=} opt_scopes Optional OAuth2 scopes to request. If not | |
| 59 * specified, the scopes specified in the manifest will be used. No consent | |
| 60 * prompt will be needed as long as the requested scopes are a subset of | |
| 61 * those already granted (in most cases, the remoting.Application framework | |
| 62 * ensures that the scopes specified in the manifest are already authorized | |
| 63 * before any application code is executed). Callers can request scopes not | |
| 64 * specified in the manifest, but a consent prompt will be shown. | |
| 65 * | |
| 58 * @return {!Promise<string>} A promise resolved with an access token | 66 * @return {!Promise<string>} A promise resolved with an access token |
| 59 * or rejected with a remoting.Error. | 67 * or rejected with a remoting.Error. |
| 60 */ | 68 */ |
| 61 remoting.Identity.prototype.getToken = function() { | 69 remoting.Identity.prototype.getToken = function(opt_scopes) { |
| 62 /** @const */ | 70 var key = getScopesKey(opt_scopes); |
| 63 var that = this; | 71 if (!this.authTokensDeferred_[key]) { |
| 64 | 72 this.authTokensDeferred_[key] = new base.Deferred(); |
| 65 if (this.authTokenDeferred_ == null) { | 73 var options = { |
| 66 this.authTokenDeferred_ = new base.Deferred(); | 74 'interactive': this.interactive_, |
|
kelvinp
2015/03/18 18:51:26
Do we need to reset interactive?
In the current im
Jamie
2015/03/18 19:08:49
Support for scopes is part of the reason I changed
kelvinp
2015/03/18 19:19:33
Acknowledged.
| |
| 67 chrome.identity.getAuthToken( | 75 'scopes': opt_scopes |
| 68 { 'interactive': this.interactive_ }, | 76 }; |
| 69 this.onAuthComplete_.bind(this)); | 77 chrome.identity.getAuthToken(options, |
| 78 this.onAuthComplete_.bind(this, opt_scopes)); | |
|
kelvinp
2015/03/18 18:51:26
Why not bind the key instead, as the first thing t
Jamie
2015/03/18 19:08:49
onAuthComplete needs both the scopes and the key,
kelvinp
2015/03/18 19:19:33
Sorry. I missed that part. Acknowledged.
| |
| 70 } | 79 } |
| 71 return this.authTokenDeferred_.promise(); | 80 return this.authTokensDeferred_[key].promise(); |
| 72 }; | 81 }; |
| 73 | 82 |
| 74 /** | 83 /** |
| 75 * Gets a fresh access token. | 84 * Gets a fresh access token. |
| 76 * | 85 * |
| 77 * @return {!Promise<string>} A promise resolved with an access token | 86 * @return {!Promise<string>} A promise resolved with an access token |
| 78 * or rejected with a remoting.Error. | 87 * or rejected with a remoting.Error. |
| 79 */ | 88 */ |
| 80 remoting.Identity.prototype.getNewToken = function() { | 89 remoting.Identity.prototype.getNewToken = function() { |
| 81 /** @type {remoting.Identity} */ | 90 /** @type {remoting.Identity} */ |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 160 */ | 169 */ |
| 161 remoting.Identity.prototype.getEmail = function() { | 170 remoting.Identity.prototype.getEmail = function() { |
| 162 return this.getUserInfo().then(function(userInfo) { | 171 return this.getUserInfo().then(function(userInfo) { |
| 163 return userInfo.email; | 172 return userInfo.email; |
| 164 }); | 173 }); |
| 165 }; | 174 }; |
| 166 | 175 |
| 167 /** | 176 /** |
| 168 * Callback for the getAuthToken API. | 177 * Callback for the getAuthToken API. |
| 169 * | 178 * |
| 179 * @param {Array<string>|undefined} scopes The explicit scopes passed to | |
| 180 * getToken, or undefined if no scopes were specified. | |
| 170 * @param {?string} token The auth token, or null if the request failed. | 181 * @param {?string} token The auth token, or null if the request failed. |
| 171 * @private | 182 * @private |
| 172 */ | 183 */ |
| 173 remoting.Identity.prototype.onAuthComplete_ = function(token) { | 184 remoting.Identity.prototype.onAuthComplete_ = function(scopes, token) { |
| 174 var authTokenDeferred = this.authTokenDeferred_; | 185 var key = getScopesKey(scopes); |
| 186 var authTokenDeferred = this.authTokensDeferred_[key]; | |
| 175 | 187 |
| 176 // Pass the token to the callback(s) if it was retrieved successfully. | 188 // Pass the token to the callback(s) if it was retrieved successfully. |
| 177 if (token) { | 189 if (token) { |
| 178 authTokenDeferred.resolve(token); | 190 var promise = this.authTokensDeferred_[key]; |
| 179 this.authTokenDeferred_ = null; | 191 delete this.authTokensDeferred_[key]; |
| 192 promise.resolve(token); | |
| 180 return; | 193 return; |
| 181 } | 194 } |
| 182 | 195 |
| 183 // If not, pass an error back to the callback(s) if we've already prompted the | 196 // If not, pass an error back to the callback(s) if we've already prompted the |
| 184 // user for permission. | 197 // user for permission. |
| 185 if (this.interactive_) { | 198 if (this.interactive_) { |
| 186 var error_message = | 199 var error_message = |
| 187 chrome.runtime.lastError ? chrome.runtime.lastError.message | 200 chrome.runtime.lastError ? chrome.runtime.lastError.message |
| 188 : 'Unknown error.'; | 201 : 'Unknown error.'; |
| 189 console.error(error_message); | 202 console.error(error_message); |
| 190 var error = (error_message == USER_CANCELLED) ? | 203 var error = (error_message == USER_CANCELLED) ? |
| 191 new remoting.Error(remoting.Error.Tag.CANCELLED) : | 204 new remoting.Error(remoting.Error.Tag.CANCELLED) : |
| 192 new remoting.Error(remoting.Error.Tag.NOT_AUTHENTICATED); | 205 new remoting.Error(remoting.Error.Tag.NOT_AUTHENTICATED); |
| 193 authTokenDeferred.reject(error); | 206 this.authTokensDeferred_[key].reject(error); |
| 194 this.authTokenDeferred_ = null; | 207 delete this.authTokensDeferred_[key]; |
| 195 return; | 208 return; |
| 196 } | 209 } |
| 197 | 210 |
| 198 // If there's no token, but we haven't yet prompted for permission, do so | 211 // If there's no token, but we haven't yet prompted for permission, do so |
| 199 // now. | 212 // now. |
| 200 var that = this; | 213 var that = this; |
| 201 var showConsentDialog = | 214 var showConsentDialog = |
| 202 (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve(); | 215 (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve(); |
| 203 showConsentDialog.then(function() { | 216 showConsentDialog.then(function() { |
| 204 that.interactive_ = true; | 217 that.interactive_ = true; |
| 205 chrome.identity.getAuthToken({'interactive': that.interactive_}, | 218 var options = { |
| 206 that.onAuthComplete_.bind(that)); | 219 'interactive': that.interactive_, |
| 220 'scopes': scopes | |
| 221 }; | |
| 222 chrome.identity.getAuthToken(options, | |
| 223 that.onAuthComplete_.bind(that, scopes)); | |
| 207 }); | 224 }); |
| 208 }; | 225 }; |
| 209 | 226 |
| 210 /** | 227 /** |
| 211 * Returns whether the web app has authenticated with the Google services. | 228 * Returns whether the web app has authenticated with the Google services. |
| 212 * | 229 * |
| 213 * @return {boolean} | 230 * @return {boolean} |
| 214 */ | 231 */ |
| 215 remoting.Identity.prototype.isAuthenticated = function() { | 232 remoting.Identity.prototype.isAuthenticated = function() { |
| 216 return remoting.identity.email_ !== ''; | 233 return remoting.identity.email_ !== ''; |
| 217 }; | 234 }; |
| 218 | 235 |
| 236 | |
| 237 /** | |
| 238 * @param {Array<string>=} opt_scopes | |
| 239 * @return {string} | |
| 240 */ | |
| 241 function getScopesKey(opt_scopes) { | |
| 242 return opt_scopes ? JSON.stringify(opt_scopes) : ''; | |
|
kelvinp
2015/03/18 18:51:26
I think we should sort the scopes first so that or
Jamie
2015/03/18 19:08:49
I was about to do this when I considered how I cou
kelvinp
2015/03/18 19:19:33
I think the complexity would only be extra line of
Jamie
2015/03/18 20:03:59
Let's keep it simple for now.
| |
| 243 } | |
| 244 | |
| 219 })(); | 245 })(); |
| OLD | NEW |