simple-oauth2
Advanced tools
Comparing version 0.3.0 to 0.4.0
# Changelog | ||
## v0.4.0 (18 January 2016) | ||
* Updated project dependencies. | ||
* Added support for passing arguments to the refresh token action. | ||
* Added project badges. | ||
* Code general cleanup and applied code styleguide. | ||
* Created CONTRIBUTING guides! (Separated from README) | ||
* Fixed bug, which resolved promises even if the token wasn´t retrieved. #64 | ||
## v0.3.0 (29 November 2015) | ||
* Better documentation! | ||
* Added support for promise based API | ||
## v0.2.1 (17 October 2014) | ||
@@ -4,0 +16,0 @@ |
16
index.js
@@ -1,2 +0,14 @@ | ||
// Convention to make this repo directly checked out into a project's deps folder | ||
module.exports = require('./lib/simple-oauth2'); | ||
var appConfig = require('./lib/config'); | ||
var utils = require('./lib/utils'); | ||
module.exports = function (config) { | ||
config = utils.configure(config, appConfig); | ||
return { | ||
authCode: require('./lib/client/auth-code')(config), | ||
password: require('./lib/client/password')(config), | ||
client: require('./lib/client/client')(config), | ||
accessToken: require('./lib/client/access-token')(config), | ||
api: require('./lib/core')(config).api | ||
}; | ||
}; |
@@ -1,52 +0,55 @@ | ||
// | ||
// ### Wrapper for the Access Token object | ||
// | ||
module.exports = function(config) { | ||
var core = require('./../core')(config); | ||
/** | ||
* Wrapper for the Access Token Object | ||
*/ | ||
module.exports = function (config) { | ||
var core = require('./../core')(config); | ||
require('date-utils'); | ||
// | ||
// ### Creates an OAuth2.AccessToken instance. | ||
// | ||
// * `token` - An object containing the token object returned from the OAuth2 server. | ||
// | ||
/** | ||
* Creates an OAuth2.AccessToken instance | ||
* @param {Object} token An object containing the token object returned from the OAuth2 server. | ||
*/ | ||
function create(token) { | ||
this.token = token; | ||
this.token.expires_at = (new Date).addSeconds(token.expires_in); | ||
return this; | ||
} | ||
// | ||
// ### Check if the access token is expired or not. | ||
// | ||
/** | ||
* Check if the access token is expired or not | ||
*/ | ||
function expired() { | ||
return (Date.compare(this.token.expires_at, new Date) == -1) ? true : false | ||
return (Date.compare(this.token.expires_at, new Date) === -1) ? true : false; | ||
} | ||
// | ||
// ### Refresh the access token | ||
// | ||
// * `callback` - The callback function returning the results. | ||
// An error object is passed as first argument and the new OAuth2.AccessToken | ||
// as last. | ||
// | ||
function refresh(callback) { | ||
var params = { grant_type: 'refresh_token', refresh_token: this.token.refresh_token }; | ||
var that = this; | ||
/** | ||
* Refresh the access token | ||
* @param {Object} An optional argument for additional API request params. | ||
* @param {Function} callback The callback function returning the results | ||
* An error object is passed as first argument and the new OAuth2.AccessToken as last | ||
*/ | ||
function refresh(params, callback) { | ||
if (typeof params === 'function') { | ||
callback = params; | ||
params = undefined; | ||
} | ||
params = params || {}; | ||
params.grant_type = 'refresh_token'; | ||
params.refresh_token = this.token.refresh_token; | ||
return core.api('POST', config.tokenPath, params).then(this.create).nodeify(callback); | ||
} | ||
// | ||
// ### Revoke access or refresh token | ||
// | ||
// * `token_type` - A String containing the type of token to revoke. | ||
// Should be either "access_token" or "refresh_token". | ||
// * `callback` - The callback function returning the results. | ||
// An error object is passed as first argument. | ||
// | ||
function revoke(token_type, callback) { | ||
var token = token_type === 'access_token' ? this.token.access_token : this.token.refresh_token; | ||
var params = { token: token, token_type_hint: token_type }; | ||
/** | ||
* Revoke access or refresh token | ||
* @param {String} tokenType A string containing the type of token to revoke. | ||
* Should be either "access_token" or "refresh_token" | ||
* @param {Function} callback The callback function returning the results. | ||
* An error object is passed as first argument | ||
*/ | ||
function revoke(tokenType, callback) { | ||
var token = tokenType === 'access_token' ? this.token.access_token : this.token.refresh_token; | ||
var params = { token: token, token_type_hint: tokenType }; | ||
@@ -57,8 +60,8 @@ return core.api('POST', config.revocationPath, params).nodeify(callback); | ||
return { | ||
'create' : create, | ||
'token' : this.token, | ||
'expired': expired, | ||
'refresh': refresh, | ||
'revoke' : revoke | ||
} | ||
create: create, | ||
token: this.token, | ||
expired: expired, | ||
refresh: refresh, | ||
revoke: revoke | ||
}; | ||
}; |
@@ -1,17 +0,17 @@ | ||
// | ||
// ### Authorization Code flow implementation | ||
// | ||
module.exports = function(config) { | ||
/** | ||
* Authorization Code flow implementation | ||
*/ | ||
module.exports = function (config) { | ||
var core = require('./../core')(config); | ||
var qs = require('querystring'); | ||
var core = require('./../core')(config), | ||
qs = require('querystring'); | ||
// ### Redirect the user to the authorization page | ||
// | ||
// * `params.redirectURI` - A String that represents the registered application URI where the | ||
// user is redirected after authorization. | ||
// * `params.scope` - A String that represents the application privileges. | ||
// * `params.state` - A String that represents an optional opaque value used by the client to | ||
// maintain state between the request and the callback. | ||
// | ||
/** | ||
* Redirect the user to the autorization page | ||
* @param {Object} params | ||
* params.redirectURI - A string that represents the registered application URI | ||
* where the user is redirected after authentication | ||
* params.scope - A String that represents the application privileges | ||
* params.state - A String that represents an option opaque value used by the client | ||
* to main the state between the request and the callback | ||
*/ | ||
function authorizeURL(params) { | ||
@@ -24,10 +24,10 @@ params.response_type = 'code'; | ||
// | ||
// ### Returns the Access Token object. | ||
// | ||
// * `params.code` - Authorization code (from previous step). | ||
// * `params.redirectURI` - A String that represents the callback uri. | ||
// * `callback` - The callback function returning the results. | ||
// An error object is passed as first argument and the result as last. | ||
// | ||
/** | ||
* Returns the Access Token Object | ||
* @param {Object} params | ||
* params.code - Authorization code (from previous step) | ||
* params.redirecURI - A string that represents the callback uri | ||
* @param {Function} callback the callback function returning the results | ||
* An error object is passed as first argument and the result as last. | ||
*/ | ||
function getToken(params, callback) { | ||
@@ -38,7 +38,6 @@ params.grant_type = 'authorization_code'; | ||
return { | ||
'authorizeURL' : authorizeURL, | ||
'getToken' : getToken | ||
} | ||
authorizeURL: authorizeURL, | ||
getToken: getToken | ||
}; | ||
}; |
@@ -1,15 +0,15 @@ | ||
// | ||
// ### Client credentials flow implementation | ||
// | ||
module.exports = function(config) { | ||
/** | ||
* Client credentials flow implementation | ||
* @param {Object} config | ||
*/ | ||
module.exports = function (config) { | ||
var core = require('./../core')(config); | ||
var core = require('./../core')(config); | ||
// | ||
// ### Returns the Access Token object. | ||
// | ||
// * `params.scope` - A String that represents the application privileges. | ||
// * `callback` - The callback function returning the results. | ||
// An error object is passed as first argument and the result as last. | ||
// | ||
/** | ||
* Returns the Access Token Object | ||
* @param {Object} params | ||
* params.scope - A string that represents the application privileges | ||
* @param {Function} callback The callback function returning the results | ||
* An error object is passed as first argument and the result as last | ||
*/ | ||
function getToken(params, callback) { | ||
@@ -20,6 +20,5 @@ params.grant_type = 'client_credentials'; | ||
return { | ||
'getToken' : getToken | ||
} | ||
getToken: getToken | ||
}; | ||
}; |
@@ -1,17 +0,16 @@ | ||
// | ||
// ### Password credentials flow implementation | ||
// | ||
module.exports = function(config) { | ||
/** | ||
* Password credentials flow implementation | ||
*/ | ||
module.exports = function (config) { | ||
var core = require('./../core')(config); | ||
var core = require('./../core')(config); | ||
// | ||
// ### Returns the Access Token object. | ||
// | ||
// * `params.username` - A string that represents the registered username. | ||
// * `params.password` - A string that represents the registered password. | ||
// * `params.scope` - A String that represents the application privileges. | ||
// * `callback` - The callback function returning the results. | ||
// An error object is passed as first argument and the result as last. | ||
// | ||
/** | ||
* Returns the Access Token Object | ||
* @param {Object} params | ||
* params.username - A string that represents the registered username | ||
* params.password - A string that represents the registered password. | ||
* params.scope - A string that represents the application privileges | ||
* @param {Function} callback The callback function returning the results | ||
* An error object is passed as first argument and the result as last. | ||
*/ | ||
function getToken(params, callback) { | ||
@@ -22,6 +21,5 @@ params.grant_type = 'password'; | ||
return { | ||
'getToken' : getToken | ||
} | ||
getToken: getToken | ||
}; | ||
}; |
module.exports = { | ||
'authorizationPath' : '/oauth/authorize', | ||
'tokenPath' : '/oauth/token', | ||
'revocationPath' : '/oauth/revoke', | ||
'clientID': null, | ||
'clientSecret': null, | ||
'site': null, | ||
'useBasicAuthorizationHeader': true, | ||
'clientSecretParameterName': 'client_secret' | ||
} | ||
authorizationPath: '/oauth/authorize', | ||
tokenPath: '/oauth/token', | ||
revocationPath: '/oauth/revoke', | ||
clientID: null, | ||
clientSecret: null, | ||
site: null, | ||
useBasicAuthorizationHeader: true, | ||
clientSecretParameterName: 'client_secret' | ||
}; |
116
lib/core.js
@@ -1,15 +0,13 @@ | ||
var exports = module.exports, | ||
crypto = require('crypto'), | ||
util = require('util'), | ||
Promise = require('bluebird'), | ||
request = Promise.promisify(require('request')); | ||
var Promise = require('bluebird'); | ||
var utils = require('./utils'); | ||
var request = Promise.promisify(require('request')); | ||
module.exports = function(config) { | ||
module.exports = function (config) { | ||
var errors = require('./error')(); | ||
// High level method to call API | ||
function api(method, path, params, callback) { | ||
var url; | ||
var isAbsoluteUrl; | ||
if (typeof params === 'function') { | ||
@@ -20,9 +18,6 @@ callback = params; | ||
if (process.env.DEBUG) console.log('OAuth2 Node Request'); | ||
utils.log('OAuth2 Node Request'); | ||
if (path.lastIndexOf('http://', 0) === 0 || path.lastIndexOf('https://', 0) === 0) { | ||
var url = path ; | ||
} else { | ||
var url = config.site + path ; | ||
} | ||
isAbsoluteUrl = path.lastIndexOf('http://', 0) === 0 || path.lastIndexOf('https://', 0) === 0; | ||
url = isAbsoluteUrl ? path : config.site + path; | ||
@@ -32,7 +27,7 @@ return call(method, url, params).spread(data).nodeify(callback); | ||
// Make the HTTP request | ||
function call(method, url, params, callback) { | ||
var header = null; | ||
var options = { uri: url, method: method }; | ||
var options = { uri: url, method: method } | ||
if (!config.clientID || !config.clientSecret || !config.site) { | ||
@@ -42,33 +37,39 @@ return Promise.reject(new Error('Configuration missing. You need to specify the client id, the client secret and the oauth2 server')); | ||
if (url && url.indexOf('access_token=') !== -1) | ||
options.headers = {} | ||
else if (params.access_token && !params.client_id) { | ||
options.headers = { 'Authorization': 'Bearer ' + params.access_token } | ||
// Token sent by querystring | ||
if (url && url.indexOf('access_token=') !== -1) { | ||
options.headers = {}; | ||
// Api authenticated call sent using headers | ||
} else if (params.access_token && !params.client_id) { | ||
options.headers = { Authorization: 'Bearer ' + params.access_token }; | ||
delete params.access_token; | ||
// OAuth2 server call used to retrieve a valid token | ||
} else if (config.useBasicAuthorizationHeader && config.clientID && !params.client_id) { | ||
options.headers = { Authorization: 'Basic ' + utils.getAuthorizationHeaderToken(config.clientID, config.clientSecret) }; | ||
} else { | ||
options.headers = {}; | ||
} | ||
else if (config.useBasicAuthorizationHeader && config.clientID && !params.client_id) | ||
options.headers = { 'Authorization': 'Basic ' + new Buffer(config.clientID + ':' + config.clientSecret).toString('base64') } | ||
else | ||
options.headers = {} | ||
if (config.headers instanceof Object) | ||
for(var header in config.headers) | ||
options.headers[header]=config.headers[header]; | ||
// Copy provided headers | ||
if (config.headers instanceof Object) { | ||
for (header in config.headers) { | ||
if (config.headers.hasOwnProperty(header)) { | ||
options.headers[header] = config.headers[header]; | ||
} | ||
} | ||
} | ||
if (config.ca) | ||
options.ca = config.ca; | ||
// Set options if provided | ||
if (config.ca) options.ca = config.ca; | ||
if (config.agent) options.agent = config.agent; | ||
if (config.rejectUnauthorized) options.rejectUnauthorized = config.rejectUnauthorized; | ||
if (typeof (config.rejectUnauthorized) != 'undefined') | ||
options.rejectUnauthorized = config.rejectUnauthorized; | ||
if (utils.isEmpty(params)) params = null; | ||
if (method !== 'GET') options.form = params; | ||
if (method === 'GET') options.qs = params; | ||
if (config.agent) | ||
options.agent = config.agent; | ||
if (isEmpty(params)) params = null; | ||
if (method != 'GET') options.form = params; | ||
if (method == 'GET') options.qs = params; | ||
// Enable the system to send authorization params in the body (for example github does not require to be in the header) | ||
if (method != 'GET' && options.form) { | ||
// Enable the system to send authorization params in the body | ||
// For example github does not require to be in the header | ||
if (method !== 'GET' && options.form) { | ||
options.form.client_id = config.clientID; | ||
@@ -78,3 +79,4 @@ options.form[config.clientSecretParameterName] = config.clientSecret; | ||
if (process.env.DEBUG) console.log('Simple OAuth2: Making the HTTP request', options) | ||
utils.log('Making the HTTP request', options); | ||
return request(options).nodeify(callback, { spread: true }); | ||
@@ -86,10 +88,13 @@ } | ||
function data(response, body, callback) { | ||
if (process.env.DEBUG) console.log('Simple OAuth2: checking response body', body); | ||
utils.log('Checking response body', body); | ||
try { body = JSON.parse(body); } | ||
catch(e) { /* The OAuth2 server does not return a valid JSON'); */ } | ||
try { | ||
body = JSON.parse(body); | ||
} catch (e) { | ||
/* The OAuth2 server does not return a valid JSON */ | ||
} | ||
if (response.statusCode >= 400 && body) { | ||
return Promise.resolve(body).nodeify(callback); | ||
}else if(response.statusCode >= 400 && !body) { | ||
return Promise.reject(body).nodeify(callback); | ||
} else if (response.statusCode >= 400 && !body) { | ||
return Promise.reject(new errors.HTTPError(response.statusCode)).nodeify(callback); | ||
@@ -101,14 +106,7 @@ } | ||
function isEmpty(ob){ | ||
for(var i in ob){ return false;} | ||
return true; | ||
} | ||
return { | ||
'call': call, | ||
'data': data, | ||
'api': api, | ||
} | ||
call: call, | ||
data: data, | ||
api: api, | ||
}; | ||
}; |
110
lib/error.js
@@ -1,66 +0,66 @@ | ||
// | ||
// A NodeJS module to handle OAuth2 server errors. | ||
// | ||
module.exports = function() { | ||
// List of all 4xx and 5xx status code plus their description | ||
var statusCodes = { | ||
400: 'Bad Request', | ||
401: 'Unauthorized', | ||
402: 'Payment Required', | ||
403: 'Forbidden', | ||
404: 'Not Found', | ||
405: 'Method Not Allowed', | ||
406: 'Not Acceptable', | ||
407: 'Proxy Authentication Required', | ||
408: 'Request Timeout', | ||
409: 'Conflict', | ||
410: 'Gone', | ||
411: 'Length Required', | ||
412: 'Precondition Failed', | ||
413: 'Request Entity Too Large', | ||
414: 'Request-URI Too Long', | ||
415: 'Unsupported Media Type', | ||
416: 'Requested Range Not Satisfiable', | ||
417: 'Expectation Failed', | ||
420: 'Enhance Your Calm', | ||
422: 'Unprocessable Entity', | ||
423: 'Locked', | ||
424: 'Failed Dependency', | ||
425: 'Unordered Collection', | ||
426: 'Upgrade Required', | ||
428: 'Precondition Required', | ||
429: 'Too Many Requests', | ||
431: 'Request Header Fields Too Large', | ||
444: 'No Response', | ||
449: 'Retry With', | ||
499: 'Client Closed Request', | ||
500: 'Internal Server Error', | ||
501: 'Not Implemented', | ||
502: 'Bad Gateway', | ||
503: 'Service Unavailable', | ||
504: 'Gateway Timeout', | ||
505: 'HTTP Version Not Supported', | ||
506: 'Variant Also Negotiates', | ||
507: 'Insufficient Storage', | ||
508: 'Loop Detected', | ||
509: 'Bandwidth Limit Exceeded', | ||
510: 'Not Extended', | ||
511: 'Network Authentication Required' | ||
}; | ||
// Personalized errror | ||
/** | ||
* A NodeJS module to handle OAuth2 server errors | ||
* @return {Object} HttpError constructor | ||
*/ | ||
module.exports = function () { | ||
function HTTPError(status) { | ||
Error.call(this); | ||
Error.captureStackTrace(this, this.constructor); | ||
this.name = this.constructor.name; | ||
this.status = status; | ||
this.message = statusCodes[status]; | ||
} HTTPError.prototype.__proto__ = Error.prototype; | ||
} | ||
HTTPError.prototype = Object.create(HTTPError.prototype); | ||
// List of all 4xx and 5xx status code plus their description | ||
var statusCodes = { | ||
400: "Bad Request", | ||
401: "Unauthorized", | ||
402: "Payment Required", | ||
403: "Forbidden", | ||
404: "Not Found", | ||
405: "Method Not Allowed", | ||
406: "Not Acceptable", | ||
407: "Proxy Authentication Required", | ||
408: "Request Timeout", | ||
409: "Conflict", | ||
410: "Gone", | ||
411: "Length Required", | ||
412: "Precondition Failed", | ||
413: "Request Entity Too Large", | ||
414: "Request-URI Too Long", | ||
415: "Unsupported Media Type", | ||
416: "Requested Range Not Satisfiable", | ||
417: "Expectation Failed", | ||
420: "Enhance Your Calm", | ||
422: "Unprocessable Entity", | ||
423: "Locked", | ||
424: "Failed Dependency", | ||
425: "Unordered Collection", | ||
426: "Upgrade Required", | ||
428: "Precondition Required", | ||
429: "Too Many Requests", | ||
431: "Request Header Fields Too Large", | ||
444: "No Response", | ||
449: "Retry With", | ||
499: "Client Closed Request", | ||
500: "Internal Server Error", | ||
501: "Not Implemented", | ||
502: "Bad Gateway", | ||
503: "Service Unavailable", | ||
504: "Gateway Timeout", | ||
505: "HTTP Version Not Supported", | ||
506: "Variant Also Negotiates", | ||
507: "Insufficient Storage", | ||
508: "Loop Detected", | ||
509: "Bandwidth Limit Exceeded", | ||
510: "Not Extended", | ||
511: "Network Authentication Required" | ||
return { | ||
HTTPError: HTTPError | ||
}; | ||
return { | ||
'HTTPError': HTTPError | ||
} | ||
}; |
{ | ||
"name": "simple-oauth2", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "Node.js client for OAuth2", | ||
"author": "Andrea Reginato <andrea.reginato@gmail.com>", | ||
"homepage": "http://github.com/andreareginato/simple-oauth2", | ||
"main": "index.js", | ||
"repository": { | ||
@@ -15,19 +16,20 @@ "type": "git", | ||
"dependencies": { | ||
"request": "~2.12.0", | ||
"querystring": "~0.1.0", | ||
"date-utils": "~1.2.12", | ||
"bluebird": "^2.10.1" | ||
"request": "~2.67.0", | ||
"bluebird": "^2.10.1", | ||
"date-utils": "~1.2.12" | ||
}, | ||
"devDependencies": { | ||
"doctoc": "^0.15.0", | ||
"mocha": "~1.8.1", | ||
"nock": "^2.13.0", | ||
"should": "~1.2.1" | ||
"eslint": "^1.10.3", | ||
"eslint-config-airbnb": "^2.1.1", | ||
"mocha": "^2.3.4", | ||
"nock": "^3.6.0", | ||
"should": "^8.0.2" | ||
}, | ||
"scripts": { | ||
"test": "mocha ", | ||
"test-watch": "mocha --watch --growl", | ||
"lint": "eslint lib/**", | ||
"test-watch": "DEBUG=true mocha --watch", | ||
"docs-gen": "doctoc README.md --github --no-title" | ||
}, | ||
"main": ".", | ||
"licenses": [ | ||
@@ -34,0 +36,0 @@ { |
@@ -1,3 +0,4 @@ | ||
[![Build Status](https://travis-ci.org/andreareginato/simple-oauth2.svg?branch=master)](https://travis-ci.org/andreareginato/simple-oauth2) | ||
[![Dependency Status](https://gemnasium.com/andreareginato/simple-oauth2.svg)](https://gemnasium.com/andreareginato/simple-oauth2) | ||
[![NPM Package Version](https://img.shields.io/npm/v/simple-oauth2.svg?style=flat-square)](https://www.npmjs.com/package/simple-oauth2) | ||
[![Build Status](https://img.shields.io/travis/andreareginato/simple-oauth2.svg?style=flat-square)](https://travis-ci.org/andreareginato/simple-oauth2) | ||
[![Dependency Status](https://img.shields.io/david/andreareginato/simple-oauth2.svg?style=flat-square)](https://david-dm.org/andreareginato/simple-oauth2) | ||
@@ -13,4 +14,4 @@ # Simple OAuth2 | ||
* Authorization Code Flow (for apps with servers that can store persistent information). | ||
* Password Credentials (when previous flow can't be used or during development). | ||
* [Authorization Code Flow](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1) (for apps with servers that can store persistent information). | ||
* [Password Credentials](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.3) (when previous flow can't be used or during development). | ||
* [Client Credentials Flow](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4) (the client can request an access token using only its client credentials) | ||
@@ -37,7 +38,2 @@ | ||
- [Contributing](#contributing) | ||
- [Releases](#releases) | ||
- [Running specs](#running-specs) | ||
- [Updating the docs](#updating-the-docs) | ||
- [Coding guidelines](#coding-guidelines) | ||
- [Feedback](#feedback) | ||
- [Authors](#authors) | ||
@@ -51,3 +47,3 @@ - [Contributors](#contributors) | ||
## Requirements | ||
Node client library is tested against Node ~0.8.x | ||
Node client library is tested against the latest minor Node versions: 0.10.x, 0.11.x, 0.12.x and 4.2.x. | ||
@@ -58,3 +54,3 @@ | ||
$ npm install simple-oauth2 | ||
$ npm install --save simple-oauth2 | ||
@@ -98,3 +94,3 @@ Install the client library using git: | ||
var code = req.query.code; | ||
console.log('/callback'); | ||
oauth2.authCode.getToken({ | ||
@@ -371,33 +367,5 @@ code: code, | ||
## Contributing | ||
Fork the repo on github and send a pull requests with topic branches to the ```develop``` branch. Do not forget to | ||
provide specs to your contribution. | ||
See [CONTRIBUTING](https://github.com/andreareginato/simple-oauth2/blob/master/CONTRIBUTING.md) | ||
### Repository | ||
* The master branch will always point to the npm latest published version. | ||
* Develop will contain the latest development/testing/new-features changes. | ||
* Every npm release will have a corresponding git tag. The **CHANGELOG.md** will be updated on every release too. | ||
### Running specs | ||
* Fork and clone the repository (`develop` branch). | ||
* Run `npm install` for dependencies. | ||
* Run `make test` to execute all specs. | ||
* Run `make test-watch` to auto execute all specs when a file change. | ||
### Updating the docs | ||
Currently, the project documentation it´s on README.md file, a table of contents is generated using a tool called [doctoc](https://github.com/thlorenz/doctoc). So if you updated this file (specially if headers are modified), please use: | ||
```bash | ||
npm run docs-gen | ||
``` | ||
### Coding guidelines | ||
Follow [github](https://github.com/styleguide/) guidelines. | ||
### Feedback | ||
Use the [issue tracker](http://github.com/andreareginato/simple-oauth2/issues) for bugs. | ||
[Mail](mailto:andrea.reginato@.gmail.com) or [Tweet](http://twitter.com/andreareginato) us | ||
for any idea that can improve the project. | ||
## Authors | ||
@@ -404,0 +372,0 @@ [Andrea Reginato](http://twitter.com/andreareginato) |
var credentials = { clientID: 'client-id', clientSecret: 'client-secret', site: 'https://example.org' }, | ||
oauth2 = require('./../lib/simple-oauth2.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
oauth2 = require('./../index.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
var request, | ||
result, resultPromise, | ||
token, tokenPromise, | ||
error, errorPromise, | ||
tokenConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com' }, | ||
refreshConfig = { 'grant_type': 'refresh_token', refresh_token: 'ec1a59d298', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
revokeConfig = { 'token': 'ec1a59d298', 'token_type_hint': 'refresh_token', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
oauthConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' }; | ||
result, resultPromise, | ||
token, tokenPromise, | ||
error, errorPromise, | ||
tokenConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com' }, | ||
refreshConfig = { 'grant_type': 'refresh_token', refresh_token: 'ec1a59d298', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
refreshWithAdditionalParamsConfig = { 'scope': 'TESTING_EXAMPLE_SCOPES', 'grant_type': 'refresh_token', refresh_token: 'ec1a59d298', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
revokeConfig = { 'token': 'ec1a59d298', 'token_type_hint': 'refresh_token', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
oauthConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' }; | ||
describe('oauth2.accessToken',function() { | ||
beforeEach(function(done) { | ||
describe('oauth2.accessToken', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
@@ -23,40 +22,34 @@ .post('/oauth/token', qs.stringify(oauthConfig)) | ||
.replyWithFile(200, __dirname + '/fixtures/access_token.json'); | ||
done(); | ||
}) | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.authCode.getToken(tokenConfig, function(e, r) { | ||
beforeEach(function (done) { | ||
oauth2.authCode.getToken(tokenConfig, function (e, r) { | ||
error = e; result = r; done(); | ||
}) | ||
}) | ||
}); | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.authCode | ||
beforeEach(function () { | ||
return oauth2.authCode | ||
.getToken(tokenConfig) | ||
.then(function(r) { resultPromise = r; }) | ||
.catch(function(e) { errorPromise = e; }) | ||
.finally(done); | ||
}) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
beforeEach(function(done) { | ||
beforeEach(function () { | ||
token = oauth2.accessToken.create(result); | ||
tokenPromise = oauth2.accessToken.create(resultPromise); | ||
done(); | ||
}); | ||
describe('#create',function() { | ||
it('creates an access token as result of callback api',function() { | ||
describe('#create', function () { | ||
it('creates an access token as result of callback api', function () { | ||
token.should.have.property('token'); | ||
}); | ||
it('created an access token as result of promise api', function() { | ||
it('created an access token as result of promise api', function () { | ||
tokenPromise.should.have.property('token'); | ||
}) | ||
}); | ||
}); | ||
describe('when not expired', function() { | ||
it('returns false',function() { | ||
describe('when not expired', function () { | ||
it('returns false', function () { | ||
token.expired().should.be.false; | ||
@@ -67,11 +60,9 @@ tokenPromise.expired().should.be.false; | ||
describe('when expired', function() { | ||
beforeEach(function(done) { | ||
describe('when expired', function () { | ||
beforeEach(function () { | ||
token.token.expires_at = Date.yesterday(); | ||
tokenPromise.token.expires_at = Date.yesterday(); | ||
done(); | ||
}); | ||
it('returns false',function() { | ||
it('returns false', function () { | ||
token.expired().should.be.true; | ||
@@ -82,5 +73,4 @@ tokenPromise.expired().should.be.true; | ||
describe('when refreshes token', function() { | ||
beforeEach(function(done) { | ||
describe('when refreshes token', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
@@ -90,8 +80,7 @@ .post('/oauth/token', qs.stringify(refreshConfig)) | ||
.replyWithFile(200, __dirname + '/fixtures/access_token.json'); | ||
done(); | ||
}); | ||
beforeEach(function(done) { | ||
beforeEach(function (done) { | ||
result = null; | ||
token.refresh(function(e, r) { | ||
token.refresh(function (e, r) { | ||
error = e; result = r; done(); | ||
@@ -101,56 +90,89 @@ }); | ||
beforeEach(function(done) { | ||
beforeEach(function () { | ||
resultPromise = null; | ||
errorPromise = null; | ||
token.refresh() | ||
return token.refresh() | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }) | ||
.finally(done); | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('makes the HTTP request', function() { | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns a new oauth2.accessToken as result of callback api',function() { | ||
it('returns a new oauth2.accessToken as result of callback api', function () { | ||
result.token.should.have.property('access_token'); | ||
}); | ||
it('returns a new oauth2.accessToken as result of promise api', function() { | ||
it('returns a new oauth2.accessToken as result of promise api', function () { | ||
resultPromise.token.should.have.property('access_token'); | ||
}); | ||
}) | ||
}); | ||
describe('#revoke',function() { | ||
describe('when refreshes token with additional params', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
.post('/oauth/token', qs.stringify(refreshWithAdditionalParamsConfig)) | ||
.times(2) | ||
.replyWithFile(200, __dirname + '/fixtures/access_token.json'); | ||
}); | ||
beforeEach(function(done) { | ||
request = nock('https://example.org:443') | ||
.post('/oauth/revoke', qs.stringify(revokeConfig)) | ||
.times(2) | ||
.reply(200); | ||
done(); | ||
beforeEach(function (done) { | ||
result = null; | ||
token.refresh({ scope: 'TESTING_EXAMPLE_SCOPES' }, function (e, r) { | ||
error = e; result = r; done(); | ||
}); | ||
}); | ||
beforeEach(function(done) { | ||
result = null; | ||
token.revoke('refresh_token', function(e) { | ||
error = e; done(); | ||
}); | ||
}); | ||
beforeEach(function () { | ||
resultPromise = null; | ||
errorPromise = null; | ||
beforeEach(function(done) { | ||
resultPromise = null; | ||
errorPromise = null; | ||
return token.refresh({ scope: 'TESTING_EXAMPLE_SCOPES' }) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
tokenPromise.revoke() | ||
.then(function(r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }) | ||
.finally(done); | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns a new oauth2.accessToken as result of callback api', function () { | ||
result.token.should.have.property('access_token'); | ||
}); | ||
it('returns a new oauth2.accessToken as result of promise api', function () { | ||
resultPromise.token.should.have.property('access_token'); | ||
}); | ||
}); | ||
describe('#revoke', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
.post('/oauth/revoke', qs.stringify(revokeConfig)) | ||
.times(2) | ||
.reply(200); | ||
}); | ||
beforeEach(function (done) { | ||
result = null; | ||
token.revoke('refresh_token', function (e) { | ||
error = e; done(); | ||
}); | ||
}); | ||
it('make HTTP call', function() { | ||
request.isDone(); | ||
beforeEach(function () { | ||
resultPromise = null; | ||
errorPromise = null; | ||
return tokenPromise.revoke() | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('make HTTP call', function () { | ||
request.isDone(); | ||
}); | ||
}); | ||
}); |
var credentials = { clientID: 'client-id', clientSecret: 'client-secret', site: 'https://example.org' }, | ||
oauth2 = require('./../lib/simple-oauth2.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
oauth2 = require('./../index.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
var request, | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com' } | ||
oauthConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
authorizeConfig = { 'redirect_uri': 'http://localhost:3000/callback', 'scope': 'user', 'state': '02afe928b' }; | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com' }, | ||
oauthConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' }, | ||
authorizeConfig = { 'redirect_uri': 'http://localhost:3000/callback', 'scope': 'user', 'state': '02afe928b' }; | ||
describe('oauth2.authCode',function() { | ||
describe('#authorizeURL', function(){ | ||
beforeEach(function(done) { | ||
describe('oauth2.authCode', function () { | ||
describe('#authorizeURL', function () { | ||
beforeEach(function () { | ||
result = oauth2.authCode.authorizeURL(authorizeConfig); | ||
done(); | ||
}) | ||
}); | ||
it('returns the authorization URI', function() { | ||
it('returns the authorization URI', function () { | ||
var expected = 'https://example.org/oauth/authorize?redirect_uri=' + encodeURIComponent('http://localhost:3000/callback') + '&scope=user&state=02afe928b&response_type=code&client_id=client-id'; | ||
result.should.eql(expected); | ||
}) | ||
}); | ||
}); | ||
describe('#getToken',function() { | ||
beforeEach(function(done) { | ||
describe('#getToken', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org') | ||
@@ -35,28 +31,26 @@ .post('/oauth/token', qs.stringify(oauthConfig)) | ||
.replyWithFile(200, __dirname + '/fixtures/access_token.json'); | ||
done(); | ||
}) | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.authCode.getToken(tokenConfig, function(e, r) { | ||
beforeEach(function (done) { | ||
oauth2.authCode.getToken(tokenConfig, function (e, r) { | ||
error = e; result = r; done(); | ||
}) | ||
}) | ||
}); | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.authCode | ||
beforeEach(function () { | ||
return oauth2.authCode | ||
.getToken(tokenConfig) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }) | ||
.finally(done); | ||
}) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('makes the HTTP request', function() { | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns an access token as result of callback api',function() { | ||
it('returns an access token as result of callback api', function () { | ||
result.should.have.property('access_token'); | ||
}); | ||
it('returns an access token as result of promise api', function() { | ||
it('returns an access token as result of promise api', function () { | ||
resultPromise.should.have.property('access_token'); | ||
@@ -63,0 +57,0 @@ }); |
var credentials = { clientID: 'client-id', clientSecret: 'client-secret', site: 'https://example.org', form: false }, | ||
oauth2 = require('./../lib/simple-oauth2.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
oauth2 = require('./../index.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
var request, | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenConfig = {}, | ||
oauthConfig = { 'grant_type': 'client_credentials', client_id: 'client-id', client_secret: 'client-secret' }; | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenConfig = {}, | ||
oauthConfig = { 'grant_type': 'client_credentials', client_id: 'client-id', client_secret: 'client-secret' }; | ||
describe('oauth2.Client',function() { | ||
describe('#getToken',function() { | ||
beforeEach(function(done) { | ||
describe('oauth2.Client', function () { | ||
describe('#getToken', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
@@ -21,31 +19,29 @@ .post('/oauth/token', qs.stringify(oauthConfig)) | ||
.replyWithFile(200, __dirname + '/fixtures/access_token.json'); | ||
done(); | ||
}) | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.client.getToken(tokenConfig, function(e, r) { | ||
beforeEach(function (done) { | ||
oauth2.client.getToken(tokenConfig, function (e, r) { | ||
error = e; result = r; done(); | ||
}) | ||
}) | ||
}); | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.client | ||
beforeEach(function () { | ||
return oauth2.client | ||
.getToken(tokenConfig) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }) | ||
.finally(done); | ||
}) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('makes the HTTP request', function() { | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns an access token as result of callback api',function() { | ||
it('returns an access token as result of callback api', function () { | ||
result.should.have.property('access_token'); | ||
}); | ||
it('returns an access token as result of promise api', function() { | ||
it('returns an access token as result of promise api', function () { | ||
resultPromise.should.have.property('access_token'); | ||
}) | ||
}); | ||
}); | ||
}); |
var credentials = { clientID: 'client-id', clientSecret: 'client-secret', site: 'https://example.org' }, | ||
oauth2 = require('./../lib/simple-oauth2.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
oauth2 = require('./../index.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
var request, | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com' }, | ||
oauthConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' }; | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com' }, | ||
oauthConfig = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' }; | ||
describe('Simple oauth2 Error',function() { | ||
describe('with status code 401',function() { | ||
beforeEach(function(done) { | ||
describe('Simple oauth2 Error', function () { | ||
describe('with status code 401', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
@@ -21,7 +19,6 @@ .post('/oauth/token', qs.stringify(oauthConfig)) | ||
.reply(401); | ||
done(); | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.authCode.getToken(tokenConfig, function(e, r) { | ||
beforeEach(function (done) { | ||
oauth2.authCode.getToken(tokenConfig, function (e, r) { | ||
error = e; result = r; done(); | ||
@@ -31,15 +28,14 @@ }); | ||
beforeEach(function(done) { | ||
oauth2.authCode | ||
beforeEach(function () { | ||
return oauth2.authCode | ||
.getToken(tokenConfig) | ||
.then(function(r) { resultPromise = r; }) | ||
.catch(function(e) { errorPromise = e; }) | ||
.finally(done); | ||
}) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('makes the HTTP request', function() { | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns an access token as result of callback api',function() { | ||
it('returns an access token as result of callback api', function () { | ||
error.message.should.eql('Unauthorized'); | ||
@@ -49,11 +45,10 @@ error.status.should.eql(401); | ||
it('returns an access token as result of promise api', function() { | ||
it('returns an access token as result of promise api', function () { | ||
errorPromise.message.should.eql('Unauthorized'); | ||
errorPromise.status.should.eql(401); | ||
}) | ||
}); | ||
}); | ||
describe('with status code 500',function() { | ||
beforeEach(function(done) { | ||
describe('with status code 500', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
@@ -63,7 +58,6 @@ .post('/oauth/token', qs.stringify(oauthConfig)) | ||
.reply(500); | ||
done(); | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.authCode.getToken(tokenConfig, function(e, r) { | ||
beforeEach(function (done) { | ||
oauth2.authCode.getToken(tokenConfig, function (e, r) { | ||
error = e; result = r; done(); | ||
@@ -73,15 +67,14 @@ }); | ||
beforeEach(function(done) { | ||
oauth2.authCode | ||
beforeEach(function () { | ||
return oauth2.authCode | ||
.getToken(tokenConfig) | ||
.then(function(r) { resultPromise = r; }) | ||
.catch(function(e) { errorPromise = e; }) | ||
.finally(done); | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('makes the HTTP request', function() { | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns an access token as result of callback api',function() { | ||
it('returns an access token as result of callback api', function () { | ||
error.message.should.eql('Internal Server Error'); | ||
@@ -91,7 +84,7 @@ error.status.should.eql(500); | ||
it('returns an access token as result of promise api', function() { | ||
it('returns an access token as result of promise api', function () { | ||
errorPromise.message.should.eql('Internal Server Error'); | ||
errorPromise.status.should.eql(500); | ||
}) | ||
}); | ||
}); | ||
}) | ||
}); |
var credentials = { clientID: 'client-id', clientSecret: 'client-secret', site: 'https://example.org' }, | ||
oauth2 = require('./../lib/simple-oauth2.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
oauth2 = require('./../index.js')(credentials), | ||
qs = require('querystring'), | ||
nock = require('nock'); | ||
var request, | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenParams = { 'username': 'alice', 'password': 'secret' }, | ||
oauthParams = { 'username': 'alice', 'password': 'secret', 'grant_type': 'password', 'client_id': 'client-id', 'client_secret': 'client-secret' }; | ||
result, resultPromise, | ||
error, errorPromise, | ||
tokenParams = { 'username': 'alice', 'password': 'secret' }, | ||
oauthParams = { 'username': 'alice', 'password': 'secret', 'grant_type': 'password', 'client_id': 'client-id', 'client_secret': 'client-secret' }; | ||
describe('oauth2.password',function() { | ||
describe('#getToken',function() { | ||
beforeEach(function(done) { | ||
describe('oauth2.password', function () { | ||
describe('#getToken', function () { | ||
beforeEach(function () { | ||
request = nock('https://example.org:443') | ||
@@ -20,27 +19,25 @@ .post('/oauth/token', qs.stringify(oauthParams)) | ||
.replyWithFile(200, __dirname + '/fixtures/access_token.json'); | ||
done(); | ||
}) | ||
}); | ||
beforeEach(function(done) { | ||
oauth2.password.getToken(tokenParams, function(e, r) { | ||
beforeEach(function (done) { | ||
oauth2.password.getToken(tokenParams, function (e, r) { | ||
error = e; result = r; done(); | ||
}) | ||
}) | ||
}); | ||
}); | ||
beforeEach(function (done) { | ||
beforeEach(function () { | ||
return oauth2.password.getToken(tokenParams) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }) | ||
.finally(done) | ||
}) | ||
.then(function (r) { resultPromise = r; }) | ||
.catch(function (e) { errorPromise = e; }); | ||
}); | ||
it('makes the HTTP request', function() { | ||
it('makes the HTTP request', function () { | ||
request.isDone(); | ||
}); | ||
it('returns an access token as result of callback api',function() { | ||
it('returns an access token as result of callback api', function () { | ||
result.should.have.property('access_token'); | ||
}); | ||
it('returns an access token as result of promises api', function() { | ||
it('returns an access token as result of promises api', function () { | ||
resultPromise.should.have.property('access_token'); | ||
@@ -47,0 +44,0 @@ }); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
41696
3
26
735
0
6
383
+ Addedansi-regex@2.1.1(transitive)
+ Addedansi-styles@2.2.1(transitive)
+ Addedasn1@0.2.6(transitive)
+ Addedassert-plus@0.2.01.0.0(transitive)
+ Addedasync@2.6.4(transitive)
+ Addedaws-sign2@0.6.0(transitive)
+ Addedbcrypt-pbkdf@1.0.2(transitive)
+ Addedbl@1.0.3(transitive)
+ Addedboom@2.10.1(transitive)
+ Addedcaseless@0.11.0(transitive)
+ Addedchalk@1.1.3(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addedcommander@2.20.3(transitive)
+ Addedcore-util-is@1.0.21.0.3(transitive)
+ Addedcryptiles@2.0.5(transitive)
+ Addeddashdash@1.14.1(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedecc-jsbn@0.1.2(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedextend@3.0.2(transitive)
+ Addedextsprintf@1.3.0(transitive)
+ Addedforever-agent@0.6.1(transitive)
+ Addedform-data@1.0.1(transitive)
+ Addedgenerate-function@2.3.1(transitive)
+ Addedgenerate-object-property@1.2.0(transitive)
+ Addedgetpass@0.1.7(transitive)
+ Addedhar-validator@2.0.6(transitive)
+ Addedhas-ansi@2.0.0(transitive)
+ Addedhawk@3.1.3(transitive)
+ Addedhoek@2.16.3(transitive)
+ Addedhttp-signature@1.1.1(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedis-my-ip-valid@1.0.1(transitive)
+ Addedis-my-json-valid@2.20.6(transitive)
+ Addedis-property@1.0.2(transitive)
+ Addedis-typedarray@1.0.0(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedisstream@0.1.2(transitive)
+ Addedjsbn@0.1.1(transitive)
+ Addedjson-schema@0.4.0(transitive)
+ Addedjson-stringify-safe@5.0.1(transitive)
+ Addedjsonpointer@5.0.1(transitive)
+ Addedjsprim@1.4.2(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addednode-uuid@1.4.8(transitive)
+ Addedoauth-sign@0.8.2(transitive)
+ Addedpinkie@2.0.4(transitive)
+ Addedpinkie-promise@2.0.1(transitive)
+ Addedprocess-nextick-args@1.0.7(transitive)
+ Addedqs@5.2.1(transitive)
+ Addedreadable-stream@2.0.6(transitive)
+ Addedrequest@2.67.0(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedsntp@1.0.9(transitive)
+ Addedsshpk@1.18.0(transitive)
+ Addedstring_decoder@0.10.31(transitive)
+ Addedstringstream@0.0.6(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedsupports-color@2.0.0(transitive)
+ Addedtough-cookie@2.2.2(transitive)
+ Addedtunnel-agent@0.4.3(transitive)
+ Addedtweetnacl@0.14.5(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedverror@1.10.0(transitive)
+ Addedxtend@4.0.2(transitive)
- Removedquerystring@~0.1.0
- Removedquerystring@0.1.0(transitive)
- Removedrequest@2.12.0(transitive)
Updatedrequest@~2.67.0