Skip to content

Commit

Permalink
Use options in httpRequest and add error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
lboyette-okta committed Sep 19, 2016
1 parent 8a7b03b commit 4b31c80
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"no-unused-vars": [2, { "args": "none" }],
"max-depth": [2, 3],
"max-len": [2, 120],
"max-params": [2, 6],
"max-params": [2, 5],
"max-statements": [2, 20],
"quotes": [2, "single"],
"semi": 2,
Expand Down
26 changes: 21 additions & 5 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ var Q = require('q');
var AuthApiError = require('./errors/AuthApiError');
var config = require('./config');

function httpRequest(sdk, url, method, args, dontSaveResponse, accessToken) {
function httpRequest(sdk, options) {
options = options || {};
var url = options.url,
method = options.method,
args = options.args,
dontSaveResponse = options.dontSaveResponse,
accessToken = options.accessToken;

var headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
Expand All @@ -17,13 +24,13 @@ function httpRequest(sdk, url, method, args, dontSaveResponse, accessToken) {
headers['Authorization'] = 'Bearer ' + accessToken;
}

var options = {
var ajaxOptions = {
headers: headers,
data: args || undefined
};

var err, res;
return new Q(sdk.options.ajaxRequest(method, url, options))
return new Q(sdk.options.ajaxRequest(method, url, ajaxOptions))
.then(function(resp) {
res = resp.responseText;
if (res && util.isString(res)) {
Expand Down Expand Up @@ -74,12 +81,21 @@ function httpRequest(sdk, url, method, args, dontSaveResponse, accessToken) {

function get(sdk, url, saveResponse) {
url = util.isAbsoluteUrl(url) ? url : sdk.options.url + url;
return httpRequest(sdk, url, 'GET', undefined, !saveResponse);
return httpRequest(sdk, {
url: url,
method: 'GET',
dontSaveResponse: !saveResponse
});
}

function post(sdk, url, args, dontSaveResponse) {
url = util.isAbsoluteUrl(url) ? url : sdk.options.url + url;
return httpRequest(sdk, url, 'POST', args, dontSaveResponse);
return httpRequest(sdk, {
url: url,
method: 'POST',
args: args,
dontSaveResponse: dontSaveResponse
});
}

module.exports = {
Expand Down
6 changes: 5 additions & 1 deletion lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ function getSession(sdk) {
}

function closeSession(sdk) {
return http.httpRequest(sdk, sdk.options.url + '/api/v1/sessions/me', 'DELETE', undefined, true);
return http.httpRequest(sdk, {
url: sdk.options.url + '/api/v1/sessions/me',
method: 'DELETE',
dontSaveResponse: true
});
}

function refreshSession(sdk) {
Expand Down
24 changes: 21 additions & 3 deletions lib/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -682,9 +682,27 @@ function getUserInfo(sdk, accessTokenObject) {
(!isToken(accessTokenObject) && !accessTokenObject.accessToken)) {
return Q.reject(new AuthSdkError('getUserInfo requires an access token object'));
}
return http.httpRequest(sdk,
sdk.options.url + '/oauth2/v1/userinfo', 'GET',
null, true, accessTokenObject.accessToken);
return http.httpRequest(sdk, {
url: sdk.options.url + '/oauth2/v1/userinfo',
method: 'GET',
dontSaveResponse: true,
accessToken: accessTokenObject.accessToken
})
.fail(function(err) {
if (err.xhr && (err.xhr.status === 401 || err.xhr.status === 403)) {
var authenticateHeader = err.xhr.getResponseHeader('WWW-Authenticate');
if (authenticateHeader) {
var errorMatches = authenticateHeader.match(/error="(.*?)"/) || [];
var errorDescriptionMatches = authenticateHeader.match(/error_description="(.*?)"/) || [];
var error = errorMatches[1];
var errorDescription = errorDescriptionMatches[1];
if (error && errorDescription) {
err = new OAuthError(error, errorDescription);
}
}
}
throw err;
});
}

module.exports = {
Expand Down
52 changes: 52 additions & 0 deletions test/spec/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -1011,5 +1011,57 @@ define(function(require) {
done();
});
});

util.itErrorsCorrectly({
title: 'returns correct error for 403',
setup: {
request: {
uri: '/oauth2/v1/userinfo',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Okta-User-Agent-Extended': 'okta-auth-js-' + packageJson.version,
'Authorization': 'Bearer ' + tokens.standardAccessToken
}
},
response: 'error-userinfo-insufficient-scope'
},
execute: function (test) {
return test.oa.token.getUserInfo(tokens.standardAccessTokenParsed);
},
expectations: function (test, err) {
expect(err.name).toEqual('OAuthError');
expect(err.message).toEqual('The access token must provide access to at least one' +
' of these scopes - profile, email, address or phone');
expect(err.errorCode).toEqual('insufficient_scope');
expect(err.errorSummary).toEqual('The access token must provide access to at least one' +
' of these scopes - profile, email, address or phone');
}
});

util.itErrorsCorrectly({
title: 'returns correct error for 401',
setup: {
request: {
uri: '/oauth2/v1/userinfo',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Okta-User-Agent-Extended': 'okta-auth-js-' + packageJson.version,
'Authorization': 'Bearer ' + tokens.standardAccessToken
}
},
response: 'error-userinfo-invalid-token'
},
execute: function (test) {
return test.oa.token.getUserInfo(tokens.standardAccessTokenParsed);
},
expectations: function (test, err) {
expect(err.name).toEqual('OAuthError');
expect(err.message).toEqual('The access token is invalid.');
expect(err.errorCode).toEqual('invalid_token');
expect(err.errorSummary).toEqual('The access token is invalid.');
}
});
});
});
5 changes: 5 additions & 0 deletions test/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ define(function(require) {

var deferred = $.Deferred();
var xhr = pair.response;

xhr.getResponseHeader = function(name) {
return xhr.headers && xhr.headers[name];
};

if (xhr.status > 0 && xhr.status < 300) {
// $.ajax send (data, textStatus, jqXHR) on success
_.defer(function () { deferred.resolve(xhr.response, null, xhr); });
Expand Down
8 changes: 8 additions & 0 deletions test/xhr/error-userinfo-insufficient-scope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
define({
"status": 403,
"responseType": "json",
"headers": {
"WWW-Authenticate": "Bearer error=\"insufficient_scope\", error_description=\"The access token must provide access to at least one of these scopes - profile, email, address or phone\""
},
"response": {}
});
8 changes: 8 additions & 0 deletions test/xhr/error-userinfo-invalid-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
define({
"status": 401,
"responseType": "json",
"headers": {
"WWW-Authenticate": "Bearer error=\"invalid_token\", error_description=\"The access token is invalid.\""
},
"response": {}
});

0 comments on commit 4b31c80

Please sign in to comment.