1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * OAuth2 API flow implementations.
12 /** @suppress {duplicate} */
13 var remoting
= remoting
|| {};
16 remoting
.OAuth2Api = function() {
20 * @return {string} OAuth2 token URL.
22 remoting
.OAuth2Api
.getOAuth2TokenEndpoint_ = function() {
23 return remoting
.settings
.OAUTH2_BASE_URL
+ '/token';
27 * @return {string} OAuth2 userinfo API URL.
29 remoting
.OAuth2Api
.getOAuth2ApiUserInfoEndpoint_ = function() {
30 return remoting
.settings
.OAUTH2_API_BASE_URL
+ '/v1/userinfo';
35 * Interprets HTTP error responses in authentication XMLHttpRequests.
38 * @param {number} xhrStatus Status (HTTP response code) of the XMLHttpRequest.
39 * @return {remoting.Error} An error code to be raised.
41 remoting
.OAuth2Api
.interpretXhrStatus_
=
43 // Return AUTHENTICATION_FAILED by default, so that the user can try to
44 // recover from an unexpected failure by signing in again.
45 /** @type {remoting.Error} */
46 var error
= remoting
.Error
.AUTHENTICATION_FAILED
;
47 if (xhrStatus
== 400 || xhrStatus
== 401 || xhrStatus
== 403) {
48 error
= remoting
.Error
.AUTHENTICATION_FAILED
;
49 } else if (xhrStatus
== 502 || xhrStatus
== 503) {
50 error
= remoting
.Error
.SERVICE_UNAVAILABLE
;
51 } else if (xhrStatus
== 0) {
52 error
= remoting
.Error
.NETWORK_FAILURE
;
54 console
.warn('Unexpected authentication response code: ' + xhrStatus
);
60 * Asynchronously retrieves a new access token from the server.
62 * @param {function(string, number): void} onDone Callback to invoke when
63 * the access token and expiration time are successfully fetched.
64 * @param {function(remoting.Error):void} onError Callback invoked if an
66 * @param {string} clientId OAuth2 client ID.
67 * @param {string} clientSecret OAuth2 client secret.
68 * @param {string} refreshToken OAuth2 refresh token to be redeemed.
69 * @return {void} Nothing.
71 remoting
.OAuth2Api
.refreshAccessToken = function(
72 onDone
, onError
, clientId
, clientSecret
, refreshToken
) {
73 /** @param {XMLHttpRequest} xhr */
74 var onResponse = function(xhr
) {
75 if (xhr
.status
== 200) {
77 // Don't use jsonParseSafe here unless you move the definition out of
78 // remoting.js, otherwise this won't work from the OAuth trampoline.
79 // TODO(jamiewalch): Fix this once we're no longer using the trampoline.
80 var tokens
= JSON
.parse(xhr
.responseText
);
81 onDone(tokens
['access_token'], tokens
['expires_in']);
83 console
.error('Invalid "token" response from server:',
84 /** @type {*} */ (err
));
85 onError(remoting
.Error
.UNEXPECTED
);
88 console
.error('Failed to refresh token. Status: ' + xhr
.status
+
89 ' response: ' + xhr
.responseText
);
90 onError(remoting
.OAuth2Api
.interpretXhrStatus_(xhr
.status
));
95 'client_id': clientId
,
96 'client_secret': clientSecret
,
97 'refresh_token': refreshToken
,
98 'grant_type': 'refresh_token'
101 remoting
.xhr
.post(remoting
.OAuth2Api
.getOAuth2TokenEndpoint_(),
102 onResponse
, parameters
);
106 * Asynchronously exchanges an authorization code for access and refresh tokens.
108 * @param {function(string, string, number): void} onDone Callback to
109 * invoke when the refresh token, access token and access token expiration
110 * time are successfully fetched.
111 * @param {function(remoting.Error):void} onError Callback invoked if an
113 * @param {string} clientId OAuth2 client ID.
114 * @param {string} clientSecret OAuth2 client secret.
115 * @param {string} code OAuth2 authorization code.
116 * @param {string} redirectUri Redirect URI used to obtain this code.
117 * @return {void} Nothing.
119 remoting
.OAuth2Api
.exchangeCodeForTokens = function(
120 onDone
, onError
, clientId
, clientSecret
, code
, redirectUri
) {
121 /** @param {XMLHttpRequest} xhr */
122 var onResponse = function(xhr
) {
123 if (xhr
.status
== 200) {
125 // Don't use jsonParseSafe here unless you move the definition out of
126 // remoting.js, otherwise this won't work from the OAuth trampoline.
127 // TODO(jamiewalch): Fix this once we're no longer using the trampoline.
128 var tokens
= JSON
.parse(xhr
.responseText
);
129 onDone(tokens
['refresh_token'],
130 tokens
['access_token'], tokens
['expires_in']);
132 console
.error('Invalid "token" response from server:',
133 /** @type {*} */ (err
));
134 onError(remoting
.Error
.UNEXPECTED
);
137 console
.error('Failed to exchange code for token. Status: ' + xhr
.status
+
138 ' response: ' + xhr
.responseText
);
139 onError(remoting
.OAuth2Api
.interpretXhrStatus_(xhr
.status
));
144 'client_id': clientId
,
145 'client_secret': clientSecret
,
146 'redirect_uri': redirectUri
,
148 'grant_type': 'authorization_code'
150 remoting
.xhr
.post(remoting
.OAuth2Api
.getOAuth2TokenEndpoint_(),
151 onResponse
, parameters
);
155 * Get the user's email address.
157 * @param {function(string):void} onDone Callback invoked when the email
158 * address is available.
159 * @param {function(remoting.Error):void} onError Callback invoked if an
161 * @param {string} token Access token.
162 * @return {void} Nothing.
164 remoting
.OAuth2Api
.getEmail = function(onDone
, onError
, token
) {
165 /** @param {XMLHttpRequest} xhr */
166 var onResponse = function(xhr
) {
167 if (xhr
.status
== 200) {
169 var result
= JSON
.parse(xhr
.responseText
);
170 onDone(result
['email']);
172 console
.error('Invalid "userinfo" response from server:',
173 /** @type {*} */ (err
));
174 onError(remoting
.Error
.UNEXPECTED
);
177 console
.error('Failed to get email. Status: ' + xhr
.status
+
178 ' response: ' + xhr
.responseText
);
179 onError(remoting
.OAuth2Api
.interpretXhrStatus_(xhr
.status
));
182 var headers
= { 'Authorization': 'OAuth ' + token
};
183 remoting
.xhr
.get(remoting
.OAuth2Api
.getOAuth2ApiUserInfoEndpoint_(),
184 onResponse
, '', headers
);