'use strict';
var uuid = require('uuid');
var qs = require('querystring');
var request = require('request');
var getHost = require('../helpers/get-host');
var setTempCookie = require('../helpers/set-temp-cookie');
module.exports = {
/**
* Consume a state token cookie.
*
* @method
* @private
*
* @param {Object} req - The http request.
* @param {Object} res - The http response.
* @return {bool} Whether or not the token was successfully verified and consumed.
*/
consumeStateToken: function (req, res) {
var oauthQueryStateToken = req.query.state;
var oauthCookieStateToken = req.cookies.oauthStateToken;
if (!oauthQueryStateToken || (!oauthCookieStateToken)) {
return false;
} else if (oauthQueryStateToken !== oauthCookieStateToken) {
return false;
}
res.clearCookie('oauthStateToken');
return true;
},
/**
* Resolve a state token from a request.
* If then token doesn't exist then a new token is created and appended onto the response.
*
* @method
* @private
*
* @param {Object} req - The http request.
* @param {Object} res - The http response.
* @return {string} A state token (UUID).
*/
resolveStateToken: function (req, res) {
var oauthStateToken = req.cookies.oauthStateToken;
Eif (!oauthStateToken) {
oauthStateToken = uuid.v4();
setTempCookie(res, 'oauthStateToken', oauthStateToken);
}
return oauthStateToken;
},
/**
* Consume a redirect uri cookie.
*
* @method
* @private
*
* @param {Object} req - The http request.
* @param {Object} res - The http response.
* @return {mixed} The redirect uri (string) or false if didn't exist.
*/
consumeRedirectUri: function (req, res) {
var redirectTo = req.cookies.oauthRedirectUri || false;
if (redirectTo) {
res.clearCookie('oauthRedirectUri');
}
return redirectTo;
},
/**
* Exchange an authentication code for an OAuth access token.
*
* @method
* @private
*
* @param {string} config - stormpathConfig object
* @param {string} code - The authentication code.
* @param {string} baseUrl - The base url to prepend to the callback uri.
* @param {Object} provider - The provider object that contains the client id, secret, and uri.
* @param {Function} callback - The callback to call once a response has been resolved.
*/
exchangeAuthCodeForAccessToken: function (req, provider, callback) {
var config = req.app.get('stormpathConfig');
var baseUrl = config.web.baseUrl || req.protocol + '://' + getHost(req);
var code = req.query.code;
var oauthStateToken = req.cookies.oauthStateToken;
var options = {
form: {
grant_type: 'authorization_code',
code: code,
redirect_uri: baseUrl + provider.uri,
client_id: config.authorizationServerClientId,
client_secret: config.authorizationServerClientSecret,
state: oauthStateToken
},
headers: {
Accept: 'application/json'
}
};
var authUrl = config.org + 'oauth2/' + config.authorizationServerId + '/v1/token';
request.post(authUrl, options, function (err, result, body) {
var parsedBody;
if (err) {
return callback(err);
}
var contentType = result.headers['content-type'] || '';
if (contentType.indexOf('text/plain') === 0) {
parsedBody = qs.parse(body);
} else {
try {
parsedBody = JSON.parse(body);
} catch (err) {
return callback(err);
}
if (parsedBody.error) {
return callback(new Error(parsedBody.error_description));
}
}
if (!parsedBody || typeof parsedBody !== 'object' || !parsedBody.access_token) {
return callback(new Error('Unable to parse response when exchanging an authorization code for an access token.'));
}
callback(null, parsedBody);
});
}
};
|