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 | 9 |
| 10 'use strict'; | 10 'use strict'; |
| (...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 /** @type {string} @private */ | 30 /** @type {string} @private */ |
| 31 this.email_ = ''; | 31 this.email_ = ''; |
| 32 /** @type {string} @private */ | 32 /** @type {string} @private */ |
| 33 this.fullName_ = ''; | 33 this.fullName_ = ''; |
| 34 /** @type {Array.<remoting.Identity.Callbacks>} */ | 34 /** @type {Promise<string>} */ |
| 35 this.pendingCallbacks_ = []; | 35 this.pendingPromise_ = null; |
| 36 }; | 36 }; |
| 37 | 37 |
| 38 /** | 38 /** |
| 39 * chrome.identity.getAuthToken should be initiated from user interactions if | 39 * chrome.identity.getAuthToken should be initiated from user interactions if |
| 40 * called with interactive equals true. This interface prompts a dialog for | 40 * called with interactive equals true. This interface prompts a dialog for |
| 41 * the user's consent. | 41 * the user's consent. |
| 42 * | 42 * |
| 43 * @interface | 43 * @interface |
| 44 */ | 44 */ |
| 45 remoting.Identity.ConsentDialog = function() {}; | 45 remoting.Identity.ConsentDialog = function() {}; |
| 46 | 46 |
| 47 /** | 47 /** |
| 48 * @return {Promise} A Promise that resolves when permission to start an | 48 * @return {Promise} A Promise that resolves when permission to start an |
| 49 * interactive flow is granted. | 49 * interactive flow is granted. |
| 50 */ | 50 */ |
| 51 remoting.Identity.ConsentDialog.prototype.show = function() {}; | 51 remoting.Identity.ConsentDialog.prototype.show = function() {}; |
| 52 | 52 |
| 53 /** | 53 /** |
| 54 * Call a function with an access token. | 54 * Get an access token. |
| 55 * | 55 * |
| 56 * @param {function(string):void} onOk Function to invoke with access token if | 56 * @return {!Promise<string>} A promise resolved the an access token or |
| 57 * an access token was successfully retrieved. | 57 * rejected with a remoting.Error. |
| 58 * @param {function(remoting.Error):void} onError Function to invoke with an | |
| 59 * error code on failure. | |
| 60 * @return {void} Nothing. | |
| 61 */ | 58 */ |
| 62 remoting.Identity.prototype.callWithToken = function(onOk, onError) { | 59 remoting.Identity.prototype.getToken = function() { |
| 63 this.pendingCallbacks_.push(new remoting.Identity.Callbacks(onOk, onError)); | 60 /** @const */ |
| 64 if (this.pendingCallbacks_.length == 1) { | 61 var that = this; |
| 65 chrome.identity.getAuthToken( | 62 |
| 66 { 'interactive': false }, | 63 if (this.pendingPromise_ == null) { |
| 67 this.onAuthComplete_.bind(this, false)); | 64 this.pendingPromise_ = new Promise(function( |
|
kelvinp
2015/02/18 19:45:24
I think base.Deferred will work better as you want
John Williams
2015/02/18 20:28:22
Done.
| |
| 65 /** function(string) */ resolve, | |
|
kelvinp
2015/02/18 19:45:24
Is this a new syntax of annotating parameters? I l
John Williams
2015/02/18 20:28:22
It's a recent addition to the syntax. I like it f
| |
| 66 /** function(remoting.Error) */ reject) { | |
| 67 chrome.identity.getAuthToken( | |
| 68 { 'interactive': false }, | |
| 69 that.onAuthComplete_.bind(that, resolve, reject, false)); | |
| 70 }); | |
| 68 } | 71 } |
| 72 return this.pendingPromise_; | |
| 69 }; | 73 }; |
| 70 | 74 |
| 71 /** | 75 /** |
| 72 * Call a function with a fresh access token. | 76 * Get a fresh access token. |
|
kelvinp
2015/02/18 19:45:24
Nit: Gets
John Williams
2015/02/18 20:28:22
Done.
| |
| 73 * | 77 * |
| 74 * @param {function(string):void} onOk Function to invoke with access token if | 78 * @return {!Promise<string>} A promise resolved the an access token or |
|
kelvinp
2015/02/18 19:45:24
s/resolved the an/resolved with an/
| |
| 75 * an access token was successfully retrieved. | 79 * rejected with a remoting.Error. |
| 76 * @param {function(remoting.Error):void} onError Function to invoke with an | |
| 77 * error code on failure. | |
| 78 * @return {void} Nothing. | |
| 79 */ | 80 */ |
| 80 remoting.Identity.prototype.callWithNewToken = function(onOk, onError) { | 81 remoting.Identity.prototype.getNewToken = function() { |
| 81 /** @type {remoting.Identity} */ | 82 /** @type {remoting.Identity} */ |
| 82 var that = this; | 83 var that = this; |
| 83 | 84 |
| 84 /** | 85 return this.getToken().then(function(/** string */ token) { |
| 85 * @param {string} token | 86 return new Promise(function(resolve, reject) { |
| 86 */ | 87 chrome.identity.removeCachedAuthToken({'token': token }, function() { |
| 87 function revokeToken(token) { | 88 resolve(that.getToken()); |
| 88 chrome.identity.removeCachedAuthToken( | 89 }); |
| 89 {'token': token }, | 90 }); |
| 90 that.callWithToken.bind(that, onOk, onError)); | 91 }); |
| 91 } | |
| 92 | |
| 93 this.callWithToken(revokeToken, onError); | |
| 94 }; | 92 }; |
| 95 | 93 |
| 96 /** | 94 /** |
| 97 * Remove the cached auth token, if any. | 95 * Remove the cached auth token, if any. |
| 98 * | 96 * |
| 99 * @param {function():void=} opt_onDone Completion callback. | 97 * @return {!Promise<null>} A promise resolved with the operation completes. |
| 100 * @return {void} Nothing. | |
| 101 */ | 98 */ |
| 102 remoting.Identity.prototype.removeCachedAuthToken = function(opt_onDone) { | 99 remoting.Identity.prototype.removeCachedAuthToken = function() { |
| 103 var onDone = (opt_onDone) ? opt_onDone : base.doNothing; | 100 return new Promise(function(resolve, reject) { |
| 104 | 101 /** @param {string} token */ |
| 105 /** @param {string} token */ | 102 var onToken = function(token) { |
| 106 var onToken = function(token) { | 103 if (token) { |
| 107 if (token) { | 104 chrome.identity.removeCachedAuthToken( |
| 108 chrome.identity.removeCachedAuthToken({'token': token}, onDone); | 105 {'token': token}, resolve.bind(null, null)); |
| 109 } else { | 106 } else { |
| 110 onDone(); | 107 resolve(null); |
| 111 } | 108 } |
| 112 }; | 109 }; |
| 113 chrome.identity.getAuthToken({'interactive': false}, onToken); | 110 chrome.identity.getAuthToken({'interactive': false}, onToken); |
| 111 }); | |
| 114 }; | 112 }; |
| 115 | 113 |
| 116 /** | 114 /** |
| 117 * Get the user's email address and full name. | 115 * Get the user's email address and full name. The full name will be |
| 118 * The full name will be null unless the webapp has requested and been | 116 * null unless the webapp has requested and been granted the |
| 119 * granted the userinfo.profile permission. | 117 * userinfo.profile permission. |
| 120 * | 118 * |
| 121 * @param {function(string,string):void} onOk Callback invoked when the user's | 119 * TODO(jrw): Type declarations say the name can't be null. Are the |
| 122 * email address and full name are available. | 120 * types wrong, or is the documentation wrong? |
| 123 * @param {function(remoting.Error):void} onError Callback invoked if an | 121 * |
| 124 * error occurs. | 122 * @return {!Promise<{email: string, name: string}>} Promise |
|
kelvinp
2015/02/18 19:45:24
no space between : and type name
John Williams
2015/02/18 20:28:22
Done.
| |
| 125 * @return {void} Nothing. | 123 * resolved with the user's email address and full name, or rejected |
| 124 * with a remoting.Error. | |
| 126 */ | 125 */ |
| 127 remoting.Identity.prototype.getUserInfo = function(onOk, onError) { | 126 remoting.Identity.prototype.getUserInfo = function() { |
| 128 if (this.isAuthenticated()) { | 127 if (this.isAuthenticated()) { |
| 129 onOk(this.email_, this.fullName_); | 128 /** |
| 130 return; | 129 * The temp variable is needed to work around a compiler bug. |
| 130 * @type {{email: string, name: string}} | |
| 131 */ | |
| 132 var result = {email: this.email_, name: this.fullName_}; | |
| 133 return Promise.resolve(result); | |
| 131 } | 134 } |
| 132 | 135 |
| 133 /** @type {remoting.Identity} */ | 136 /** @type {remoting.Identity} */ |
| 134 var that = this; | 137 var that = this; |
| 135 | 138 |
| 136 /** | 139 return this.getToken().then(function(token) { |
| 137 * @param {string} email | 140 return new Promise(function(resolve, reject) { |
| 138 * @param {string} name | 141 /** |
| 139 */ | 142 * @param {string} email |
| 140 var onResponse = function(email, name) { | 143 * @param {string} name |
| 141 that.email_ = email; | 144 */ |
| 142 that.fullName_ = name; | 145 var onResponse = function(email, name) { |
| 143 onOk(email, name); | 146 that.email_ = email; |
| 144 }; | 147 that.fullName_ = name; |
| 148 resolve({email: email, name: name}); | |
| 149 }; | |
| 145 | 150 |
| 146 this.callWithToken( | 151 remoting.oauth2Api.getUserInfo(onResponse, reject, token); |
| 147 remoting.oauth2Api.getUserInfo.bind( | 152 }); |
| 148 remoting.oauth2Api, onResponse, onError), | 153 }); |
| 149 onError); | |
| 150 }; | 154 }; |
| 151 | 155 |
| 152 /** | 156 /** |
| 153 * Get the user's email address. | 157 * Get the user's email address. |
| 154 * | 158 * |
| 155 * @param {function(string):void} onOk Callback invoked when the email | 159 * @return {!Promise<string>} Promise resolved with the user's email |
| 156 * address is available. | 160 * address or rejected with a remoting.Error. |
| 157 * @param {function(remoting.Error):void} onError Callback invoked if an | |
| 158 * error occurs. | |
| 159 * @return {void} Nothing. | |
| 160 */ | 161 */ |
| 161 remoting.Identity.prototype.getEmail = function(onOk, onError) { | 162 remoting.Identity.prototype.getEmail = function() { |
| 162 this.getUserInfo(function(email, name) { | 163 this.getUserInfo().then(function(userInfo) { |
| 163 onOk(email); | 164 return userInfo.email; |
| 164 }, onError); | 165 }); |
| 165 }; | 166 }; |
| 166 | 167 |
| 167 /** | 168 /** |
| 168 * Get the user's email address, or null if no successful call to getUserInfo | 169 * Get the user's email address, or null if no successful call to getUserInfo |
| 169 * has been made. | 170 * has been made. |
| 170 * | 171 * |
| 171 * @return {?string} The cached email address, if available. | 172 * @return {?string} The cached email address, if available. |
| 172 */ | 173 */ |
| 173 remoting.Identity.prototype.getCachedEmail = function() { | 174 remoting.Identity.prototype.getCachedEmail = function() { |
| 174 return this.email_; | 175 return this.email_; |
| 175 }; | 176 }; |
| 176 | 177 |
| 177 /** | 178 /** |
| 178 * Get the user's full name. | 179 * Get the user's full name. |
| 179 * This will return null if either: | 180 * This will return null if either: |
| 180 * No successful call to getUserInfo has been made, or | 181 * No successful call to getUserInfo has been made, or |
| 181 * The webapp doesn't have permission to access this value. | 182 * The webapp doesn't have permission to access this value. |
| 182 * | 183 * |
| 183 * @return {?string} The cached user's full name, if available. | 184 * @return {?string} The cached user's full name, if available. |
| 184 */ | 185 */ |
| 185 remoting.Identity.prototype.getCachedUserFullName = function() { | 186 remoting.Identity.prototype.getCachedUserFullName = function() { |
| 186 return this.fullName_; | 187 return this.fullName_; |
| 187 }; | 188 }; |
| 188 | 189 |
| 189 /** | 190 /** |
| 190 * Callback for the getAuthToken API. | 191 * Callback for the getAuthToken API. |
| 191 * | 192 * |
| 193 * @param {function(string):void} resolve | |
| 194 * @param {function(remoting.Error):void} reject | |
| 192 * @param {boolean} interactive The value of the "interactive" parameter to | 195 * @param {boolean} interactive The value of the "interactive" parameter to |
| 193 * getAuthToken. | 196 * getAuthToken. |
| 194 * @param {?string} token The auth token, or null if the request failed. | 197 * @param {?string} token The auth token, or null if the request failed. |
| 195 * @private | 198 * @private |
| 196 */ | 199 */ |
| 197 remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) { | 200 remoting.Identity.prototype.onAuthComplete_ = function( |
| 201 resolve, reject, interactive, token) { | |
|
kelvinp
2015/02/18 19:45:24
As commented earlier, use base.Deferred instead
John Williams
2015/02/18 20:28:22
Done.
| |
| 198 // Pass the token to the callback(s) if it was retrieved successfully. | 202 // Pass the token to the callback(s) if it was retrieved successfully. |
| 199 if (token) { | 203 if (token) { |
| 200 while (this.pendingCallbacks_.length > 0) { | 204 resolve(token); |
| 201 var callback = /** @type {remoting.Identity.Callbacks} */ | 205 this.pendingPromise_ = null; |
| 202 (this.pendingCallbacks_.shift()); | |
| 203 callback.onOk(token); | |
| 204 } | |
| 205 return; | 206 return; |
| 206 } | 207 } |
| 207 | 208 |
| 208 // If not, pass an error back to the callback(s) if we've already prompted the | 209 // If not, pass an error back to the callback(s) if we've already prompted the |
| 209 // user for permission. | 210 // user for permission. |
| 210 if (interactive) { | 211 if (interactive) { |
| 211 var error_message = | 212 var error_message = |
| 212 chrome.runtime.lastError ? chrome.runtime.lastError.message | 213 chrome.runtime.lastError ? chrome.runtime.lastError.message |
| 213 : 'Unknown error.'; | 214 : 'Unknown error.'; |
| 214 console.error(error_message); | 215 console.error(error_message); |
| 215 while (this.pendingCallbacks_.length > 0) { | 216 reject(remoting.Error.NOT_AUTHENTICATED); |
| 216 var callback = /** @type {remoting.Identity.Callbacks} */ | |
| 217 (this.pendingCallbacks_.shift()); | |
| 218 callback.onError(remoting.Error.NOT_AUTHENTICATED); | |
| 219 } | |
| 220 return; | 217 return; |
| 221 } | 218 } |
| 222 | 219 |
| 223 // If there's no token, but we haven't yet prompted for permission, do so | 220 // If there's no token, but we haven't yet prompted for permission, do so |
| 224 // now. | 221 // now. |
| 225 var that = this; | 222 var that = this; |
| 226 var showConsentDialog = | 223 var showConsentDialog = |
| 227 (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve(); | 224 (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve(); |
| 228 showConsentDialog.then(function() { | 225 showConsentDialog.then(function() { |
| 229 chrome.identity.getAuthToken({'interactive': true}, | 226 chrome.identity.getAuthToken({'interactive': true}, |
| 230 that.onAuthComplete_.bind(that, true)); | 227 that.onAuthComplete_.bind( |
| 228 that, resolve, reject, true)); | |
|
kelvinp
2015/02/18 19:45:24
indentation
John Williams
2015/02/18 20:28:22
Done.
| |
| 231 }); | 229 }); |
| 232 }; | 230 }; |
| 233 | 231 |
| 234 /** | 232 /** |
| 235 * Internal representation for pair of callWithToken callbacks. | |
| 236 * | |
| 237 * @param {function(string):void} onOk | |
| 238 * @param {function(remoting.Error):void} onError | |
| 239 * @constructor | |
| 240 * @private | |
| 241 */ | |
| 242 remoting.Identity.Callbacks = function(onOk, onError) { | |
| 243 /** @type {function(string):void} */ | |
| 244 this.onOk = onOk; | |
| 245 /** @type {function(remoting.Error):void} */ | |
| 246 this.onError = onError; | |
| 247 }; | |
| 248 | |
| 249 /** | |
| 250 * Returns whether the web app has authenticated with the Google services. | 233 * Returns whether the web app has authenticated with the Google services. |
| 251 * | 234 * |
| 252 * @return {boolean} | 235 * @return {boolean} |
| 253 */ | 236 */ |
| 254 remoting.Identity.prototype.isAuthenticated = function() { | 237 remoting.Identity.prototype.isAuthenticated = function() { |
| 255 return remoting.identity.email_ !== ''; | 238 return remoting.identity.email_ !== ''; |
| 256 }; | 239 }; |
| OLD | NEW |