Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

simple-oauth2

Package Overview
Dependencies
Maintainers
2
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

simple-oauth2 - npm Package Compare versions

Comparing version 1.5.0 to 1.5.1

lib/encoding.js

27

CHANGELOG.md
# Changelog
## Next
* Replace internal request library to wreck
* Replace bluebird with native promise implementation
* Replace callback interface with async/await
## v1.5.1
* Add support to specify scopes as array in `getToken` method
* Add support to empty strings and visual ASCII characters on `clientId`/`clientSecret` options
## v1.5.0
* Update debug dependency

@@ -8,2 +20,3 @@ * Add support to encode the authorization headers

## v1.4.0
* Update dependencies

@@ -13,20 +26,27 @@ * Add Node 8 to test matrix

## v1.3.0
* Add support for custom idParamName in authCode.authorizeURL() method
## v1.2.0
* Upgrade dependencies, to avoid using outdated/vulnerable versions
## v1.1.0
* Add support to body encoding format in library requests
## v1.0.3
* Add missing documentation for module options
## v1.0.2
* Parse token payload response `expires_in` property as integer
## v1.0.1
* Fixed documentation for **client** option.
## v1.0.0
* Refactored test to use fixtures.

@@ -43,2 +63,3 @@ * Update code to comply with more linter rules.

## v0.8.0 (1 August 2016)
* Upgraded code to strict mode.

@@ -50,2 +71,3 @@ * Upgraded all the code base to es6.

## v0.7.0 (22 April 2016)
* Replaced internal logger by the debug module logger.

@@ -55,2 +77,3 @@ * Fixed some project metadata.

## v0.6.0 (04 April 2016)
* Added optional sending support to the body auth params.

@@ -62,8 +85,11 @@ * Updated license information.

## v0.5.1 (25 January 2016)
* Fixed error class prototype inheritance. Now inherits correctly from Error.
## v0.5.0 (22 January 2016)
* Now all error states returned from the server, are rejected as HTTPError instances. (This allow to know what httpStatusCode was returned)
## v0.4.0 (18 January 2016)
* Updated project dependencies.

@@ -77,2 +103,3 @@ * Added support for passing arguments to the refresh token action.

## v0.3.0 (29 November 2015)
* Better documentation!

@@ -79,0 +106,0 @@ * Added support for promise based API

35

index.js

@@ -9,2 +9,5 @@ 'use strict';

// https://tools.ietf.org/html/draft-ietf-oauth-v2-31#appendix-A.1
const vsCharRegEx = /^[\x20-\x7E]*$/;
const optionsSchema = Joi

@@ -14,4 +17,4 @@ .object()

client: Joi.object().keys({
id: Joi.string().required(),
secret: Joi.string().required(),
id: Joi.string().regex(vsCharRegEx).allow(''),
secret: Joi.string().regex(vsCharRegEx).allow(''),
secretParamName: Joi.string().default('client_secret'),

@@ -33,5 +36,4 @@ idParamName: Joi.string().default('client_id'),

options: Joi.object().keys({
bodyFormat: Joi.any().valid('form', 'json').default('form'),
useBasicAuthorizationHeader: Joi.boolean().default(true),
useBodyAuth: Joi.boolean().default(true),
bodyFormat: Joi.any().only('form', 'json').default('form'),
authorizationMethod: Joi.any().only('header', 'body').default('header'),
}).default(),

@@ -43,21 +45,16 @@ });

/**
* Creates a new simple-oauth2 client
* with the passed configuration
*
* @param {Object} options Module options as defined in schema
* Creates a new simple-oauth2 client with the provided configuration
* @param {Object} opts Module options as defined in schema
* @returns {Object} The simple-oauth2 client
*/
create(options) {
const moduleOptions = Joi.attempt(
options || {},
optionsSchema,
'Invalid options provided to simple-oauth2'
);
create(opts = {}) {
const options = Joi.attempt(opts, optionsSchema, 'Invalid options provided to simple-oauth2');
return {
authorizationCode: authCodeModule(moduleOptions),
ownerPassword: passwordModule(moduleOptions),
clientCredentials: clientCredentialsModule(moduleOptions),
accessToken: accessTokenModule(moduleOptions),
accessToken: accessTokenModule(options),
ownerPassword: passwordModule(options),
authorizationCode: authCodeModule(options),
clientCredentials: clientCredentialsModule(options),
};
},
};
'use strict';
const url = require('url');
const addSeconds = require('date-fns/add_seconds');

@@ -15,75 +14,65 @@ const isAfter = require('date-fns/is_after');

const core = coreModule(config);
const tokenUrl = url.resolve(config.auth.tokenHost, config.auth.tokenPath);
const revokeUrl = url.resolve(config.auth.tokenHost, config.auth.revokePath);
function AccessToken(token) {
this.token = token;
class AccessToken {
constructor(token) {
this.token = token;
if ('expires_at' in this.token) {
if (!isDate(this.token.expires_at)) {
this.token.expires_at = parse(this.token.expires_at);
if ('expires_at' in this.token) {
if (!isDate(this.token.expires_at)) {
this.token.expires_at = parse(this.token.expires_at);
}
} else {
this.token.expires_at = addSeconds(
new Date(),
Number.parseInt(token.expires_in, 10)
);
}
} else {
this.token.expires_at = addSeconds(
new Date(),
Number.parseInt(token.expires_in, 10)
);
}
}
/**
* Creates an OAuth2.AccessToken instance
* @param {Object} token An object containing the token object returned from the OAuth2 server.
*/
function createAccessToken(tokenToUse) {
return new AccessToken(tokenToUse);
}
/**
* Check if the access token is expired or not
*/
expired() {
return isAfter(new Date(), this.token.expires_at);
}
/**
* Check if the access token is expired or not
*/
AccessToken.prototype.expired = function expired() {
return isAfter(new Date(), this.token.expires_at);
};
/**
* Refresh the access token
* @param {Object} params An optional argument for additional API request params.
*/
async refresh(params) {
const options = Object.assign({}, params, {
grant_type: 'refresh_token',
refresh_token: this.token.refresh_token,
});
/**
* Refresh the access token
* @param {Object} An optional argument for additional API request params.
* @param {Function} callback
*/
AccessToken.prototype.refresh = function refresh(params, callback) {
if (typeof params === 'function') {
callback = params;
params = undefined;
const response = await core.request(config.auth.tokenPath, options);
return createAccessToken(response);
}
const options = Object.assign({}, params || {}, {
grant_type: 'refresh_token',
refresh_token: this.token.refresh_token,
});
/**
* 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"
*/
async revoke(tokenType) {
const token = tokenType === 'access_token' ? this.token.access_token : this.token.refresh_token;
const options = {
token,
token_type_hint: tokenType,
};
return core
.api('POST', tokenUrl, options)
.then(response => createAccessToken(response))
.nodeify(callback);
};
return core.request(config.auth.revokePath, options);
}
}
/**
* 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
*/
AccessToken.prototype.revoke = function revoke(tokenType, callback) {
const token = tokenType === 'access_token' ? this.token.access_token : this.token.refresh_token;
const options = {
token,
token_type_hint: tokenType,
};
* Creates an OAuth2.AccessToken instance
* @param {Object} token An object containing the token object returned from the OAuth2 server.
*/
function createAccessToken(token) {
return new AccessToken(token);
}
return core
.api('POST', revokeUrl, options)
.nodeify(callback);
};
return {

@@ -90,0 +79,0 @@ create: createAccessToken,

@@ -12,3 +12,2 @@ 'use strict';

const core = coreModule(config);
const tokenUrl = url.resolve(config.auth.tokenHost, config.auth.tokenPath);
const authorizeUrl = url.resolve(config.auth.authorizeHost, config.auth.authorizePath);

@@ -20,3 +19,4 @@

* where the user is redirected after authentication
* @param {String} params.scope A String that represents the application privileges
* @param {String|Array<String>} params.scope A String or array of strings
* that represents the application privileges
* @param {String} params.state A String that represents an option opaque value used by the client

@@ -26,8 +26,14 @@ * to main the state between the request and the callback

*/
function authorizeURL(params) {
const options = Object.assign({}, {
function authorizeURL(params = {}) {
const baseParams = {
response_type: 'code',
[config.client.idParamName]: config.client.id,
}, params);
};
if (Array.isArray(params.scope)) {
params.scope = params.scope.join(',');
}
const options = Object.assign({}, baseParams, params);
return `${authorizeUrl}?${qs.stringify(options)}`;

@@ -40,6 +46,5 @@ }

* @param {String} params.redirecURI A string that represents the callback uri
* @param {Function} callback
* @return {Promise}
*/
function getToken(params, callback) {
async function getToken(params) {
const options = Object.assign({}, params, {

@@ -49,5 +54,3 @@ grant_type: 'authorization_code',

return core
.api('POST', tokenUrl, options)
.nodeify(callback);
return core.request(config.auth.tokenPath, options);
}

@@ -54,0 +57,0 @@

'use strict';
const url = require('url');
const coreModule = require('./../core');

@@ -11,18 +10,15 @@

const core = coreModule(config);
const tokenUrl = url.resolve(config.auth.tokenHost, config.auth.tokenPath);
/**
* Returns the Access Token Object
* @param {Object} params
* @param {String} params.scope A string that represents the application privileges
* @param {Function} callback
* @return {Promise}
*/
function getToken(params, callback) {
const options = Object.assign({}, params || {}, {
async function getToken(params) {
const options = Object.assign({}, params, {
grant_type: 'client_credentials',
});
return core
.api('POST', tokenUrl, options)
.nodeify(callback);
return core.request(config.auth.tokenPath, options);
}

@@ -29,0 +25,0 @@

'use strict';
const url = require('url');
const coreModule = require('./../core');

@@ -11,20 +10,17 @@

const core = coreModule(config);
const tokenUrl = url.resolve(config.auth.tokenHost, config.auth.tokenPath);
/**
* Returns the Access Token Object
* @param {Object} params
* @param {String} params.username A string that represents the registered username
* @param {String} params.password A string that represents the registered password.
* @param {String} params.password A string that represents the registered password
* @param {String} params.scope A string that represents the application privileges
* @param {Function} callback
* @return {Promise}
*/
function getToken(params, callback) {
const options = Object.assign({}, params || {}, {
async function getToken(params) {
const options = Object.assign({}, params, {
grant_type: 'password',
});
return core
.api('POST', tokenUrl, options)
.nodeify(callback);
return core.request(config.auth.tokenPath, options);
}

@@ -31,0 +27,0 @@

'use strict';
const Promise = require('bluebird');
const debug = require('debug')('simple-oauth2:main');
const utils = require('./utils');
const HTTPError = require('./error');
const Wreck = require('wreck');
const querystring = require('querystring');
const debug = require('debug')('simple-oauth2:index');
const encoding = require('./encoding');
const request = Promise.promisify(require('request'), {
multiArgs: true,
});
module.exports = (config) => {
const httpOptions = Object.assign({}, config.http, {
baseUrl: config.auth.tokenHost,
});
/**
* Parse the oauth server response
* Decides wether or not the response is accepted
* @param {response} response raw response object
* @param {Object} body
* @param {Function} callback
* @return {Promise}
*/
function parseReponse(response, body) {
debug('Checking response body', body);
const wreck = Wreck.defaults(httpOptions);
try {
body = JSON.parse(body);
} catch (e) {
/* The OAuth2 server does not return a valid JSON */
}
async function request(url, params) {
let payload = params;
const options = {
json: true,
headers: {},
};
if (response.statusCode >= 400) {
return Promise.reject(new HTTPError(response.statusCode, body));
}
if (config.options.authorizationMethod === 'header') {
const basicHeader = encoding.getAuthorizationHeaderToken(
config.client.id,
config.client.secret
);
return Promise.resolve(body);
}
debug('Using header authentication. Authorization header set to %s', basicHeader);
module.exports = (config) => {
// makes an http request
function call(method, uri, params) {
const options = Object.assign({}, { method, uri }, config.http);
options.headers.Authorization = `Basic ${basicHeader}`;
} else {
debug('Using body authentication');
// api authenticated call sent using headers
if (params.access_token && !params[config.client.idParamName]) {
options.headers.Authorization = `Bearer ${params.access_token}`;
delete params.access_token;
// oauth2 server call used to retrieve a valid token
} else if (config.options.useBasicAuthorizationHeader &&
config.client.id &&
!params[config.client.idParamName]) {
const basicHeader = utils.getAuthorizationHeaderToken(config.client.id, config.client.secret);
options.headers.Authorization = `Basic ${basicHeader}`;
payload = Object.assign({}, payload, {
[config.client.idParamName]: config.client.id,
[config.client.secretParamName]: config.client.secret,
});
}
if (Object.keys(params).length === 0) params = null;
if (config.options.bodyFormat === 'form') {
debug('Using form request format');
if (method !== 'GET') {
if (config.options.bodyFormat === 'form') {
options.form = params;
} else {
// if the bodyFormat is not form, is assummed to be json
options.json = true;
options.body = params;
}
// An example using `form` authorization params in the body is the
// GitHub API.
options.payload = querystring.stringify(payload);
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
} else {
options.qs = params;
}
debug('Using json request format');
// Enable the system to send authorization params in the body.
if (config.options.useBodyAuth) {
if (options.form) {
// An example using `form` authorization params in the body is the
// GitHub API.
options.form[config.client.idParamName] = config.client.id;
options.form[config.client.secretParamName] = config.client.secret;
} else {
// An example using `json` authorization params in the body is the
// Amazon Developer Publishing API.
options.body[config.client.idParamName] = config.client.id;
options.body[config.client.secretParamName] = config.client.secret;
}
// An example using `json` authorization params in the body is the
// Amazon Developer Publishing API.
options.payload = payload;
options.headers['Content-Type'] = 'application/json';
}
debug('Making the HTTP request', options);
debug('Creating request to: (POST) %s', url);
debug('Using options: %j', options);
return request(options);
}
const result = await wreck.post(url, options);
// High level method to call API
function api(method, url, params, callback) {
if (typeof params === 'function') {
callback = params;
params = {};
}
debug('OAuth2 Node Request');
return call(method, url, params)
.spread(parseReponse)
.nodeify(callback);
return result.payload;
}
return {
call,
api,
request,
};
};
{
"name": "simple-oauth2",
"version": "1.5.0",
"version": "1.5.1",
"description": "Node.js client for OAuth2",

@@ -16,3 +16,3 @@ "author": "Andrea Reginato <andrea.reginato@gmail.com>",

"engine": {
"node": ">=4.0"
"node": ">=8.0"
},

@@ -29,7 +29,6 @@ "scripts": {

"dependencies": {
"bluebird": "^3.5.0",
"date-fns": "^1.3.0",
"debug": "^3.1.0",
"joi": "^12.0.0",
"request": "^2.81.0"
"joi": "^13.0.2",
"wreck": "^14.0.2"
},

@@ -36,0 +35,0 @@ "devDependencies": {

@@ -0,1 +1,3 @@

# Simple OAuth2
[![NPM Package Version](https://img.shields.io/npm/v/simple-oauth2.svg?style=flat-square)](https://www.npmjs.com/package/simple-oauth2)

@@ -5,6 +7,4 @@ [![Build Status](https://img.shields.io/travis/lelylan/simple-oauth2.svg?style=flat-square)](https://travis-ci.org/lelylan/simple-oauth2)

# Simple OAuth2
Node.js client library for [OAuth2](http://oauth.net/2/).
Node.js client library for [OAuth2](http://oauth.net/2/) (this library supports both callbacks or promises for async flow).
OAuth2 lets users grant the access to the desired resources to third party applications,

@@ -26,3 +26,2 @@ giving them the possibility to enable and disable those accesses whenever they want.

## Table of Contents

@@ -55,38 +54,40 @@

## Requirements
Node client library is tested against the latest minor Node versions: 4, 5 and 6.
To use in older node version, please use [simple-oauth2@0.x](https://github.com/lelylan/simple-oauth2/tree/v0.8.0).
The node client library is tested against the latest Node 8 LTS and newer versions.
To use in node 4, 5 or 6, please use [simple-oauth2@1.x](https://github.com/lelylan/simple-oauth2/tree/1.5.0). Older node versions are unsupported.
## Getting started
### Installation
Install the client library using [npm](http://npmjs.org/):
```bash
$ npm install --save simple-oauth2
```
```bash
npm install --save simple-oauth2
```
### Options
Simple OAuth2 accepts an object with the following valid params.
* `client` - required object with the following properties:
- `id` - Service registered client id. Required.
- `secret` - Service registered client secret. Required.
- `secretParamName` - Parameter name used to send the client secret. Default to **client_secret**.
- `idParamName` - Parameter name used to send the client id. Default to **client_id**.
* `id` - Service registered client id. Required.
* `secret` - Service registered client secret. Required.
* `secretParamName` - Parameter name used to send the client secret. Default to **client_secret**.
* `idParamName` - Parameter name used to send the client id. Default to **client_id**.
* `auth` - required object with the following properties.
- `tokenHost` - String used to set the host to request the tokens to. Required.
- `tokenPath` - String path to request an access token. Default to **/oauth/token**.
- `revokePath` - String path to revoke an access token. Default to **/oauth/revoke**.
- `authorizeHost` - String used to set the host to request an "authorization code". Default to the value set on `auth.tokenHost`.
- `authorizePath` - String path to request an authorization code. Default to **/oauth/authorize**.
* `tokenHost` - String used to set the host to request the tokens to. Required.
* `tokenPath` - String path to request an access token. Default to **/oauth/token**.
* `revokePath` - String path to revoke an access token. Default to **/oauth/revoke**.
* `authorizeHost` - String used to set the host to request an "authorization code". Default to the value set on `auth.tokenHost`.
* `authorizePath` - String path to request an authorization code. Default to **/oauth/authorize**.
* `http` optional object used to set global options to the internal http library (request-js).
- Any key is allowed here. Default to `headers.Accept = application/json`.
* `http` optional object used to set global options to the internal http library ([wreck](https://github.com/hapijs/wreck)).
* All options except **baseUrl** are allowed. Default to `headers.Accept = application/json`.
* `options` optional object to setup the module.
- `bodyFormat` - Format of data sent in the request body. Valid values are `form` or `json`. Defaults to **form**.
- `useBodyAuth` - Whether or not the client.id/client.secret params are sent in the request body. Defaults to **true**.
- `useBasicAuthorizationHeader` - Whether or not the Basic Authorization header should be sent at the token request.
* `bodyFormat` - Format of data sent in the request body. Valid options are `form` or `json`. Defaults to **form**.
* `authorizationMethod` - Indicates the method used to send the client.id/client.secret authorization params at the token request. Valid options are `header` or `body`. If set to **body**, the **bodyFormat** option will be used to format the credentials. Defaults to **header**.

@@ -110,5 +111,7 @@ ```javascript

### Example of Usage
See the [example folder](./example).
## OAuth2 Supported flows
### Authorization Code flow

@@ -127,3 +130,3 @@

redirect_uri: 'http://localhost:3000/callback',
scope: '<scope>',
scope: '<scope>', // also can be an array of multiple scopes, ex. ['<scope1>, '<scope2>', '...']
state: '<state>'

@@ -141,21 +144,10 @@ });

// Callbacks
// Save the access token
oauth2.authorizationCode.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
try {
const result = await oauth2.authorizationCode.getToken(tokenConfig)
const accessToken = oauth2.accessToken.create(result);
});
} catch (error) {
console.log('Access Token Error', error.message);
}
// Promises
// Save the access token
oauth2.authorizationCode.getToken(tokenConfig)
.then((result) => {
const accessToken = oauth2.accessToken.create(result);
})
.catch((error) => {
console.log('Access Token Error', error.message);
});
```

@@ -176,24 +168,12 @@

username: 'username',
password: 'password' 
password: 'password'
};
// Callbacks
// Save the access token
oauth2.ownerPassword.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
try {
const result = await oauth2.ownerPassword.getToken(tokenConfig);
const accessToken = oauth2.accessToken.create(result);
});
// Promises
// Save the access token
oauth2.ownerPassword
.getToken(tokenConfig)
.then((result) => {
const accessToken = oauth2.accessToken.create(result);
return accessToken;
});
} catch (error) {
console.log('Access Token Error', error.message);
}
```

@@ -209,26 +189,13 @@

// Callbacks
// Get the access token object for the client
oauth2.clientCredentials.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
try {
const result = await oauth2.clientCredentials.getToken(tokenConfig);
const accessToken = oauth2.accessToken.create(result);
});
// Promises
// Get the access token object for the client
oauth2.clientCredentials
.getToken(tokenConfig)
.then((result) => {
const accessToken = oauth2.accessToken.create(result);
})
.catch((error) => {
console.log('Access Token error', error.message);
});
} catch (error) {
console.log('Access Token error', error.message);
}
```
## Helpers
### Access Token object

@@ -253,12 +220,7 @@

if (accessToken.expired()) {
// Callbacks
accessToken.refresh((error, result) => {
accessToken = result;
})
// Promises
accessToken.refresh()
.then((result) => {
accessToken = result;
});
try {
accessToken = await accessToken.refresh();
} catch (error) {
console.log('Error refreshing access token: ', error.message);
}
}

@@ -287,12 +249,7 @@ ```

if (shouldRefresh) {
// Callbacks
accessToken.refresh((error, result) => {
accessToken = result;
})
// Promises
accessToken.refresh()
.then((result) => {
accessToken = result;
});
try {
accessToken = await accessToken.refresh();
} catch (error) {
console.log('Error refreshing access token: ', error.message);
}
}

@@ -306,26 +263,13 @@ ```

// Callbacks
// Revoke only the access token
accessToken.revoke('access_token', (error) => {
// Revoke both access and refresh tokens
try {
// Revoke only the access token
await accessToken.revoke('access_token')
// Session ended. But the refresh_token is still valid.
// Revoke the refresh_token
accessToken.revoke('refresh_token', (error) => {
console.log('token revoked.');
});
});
// Promises
// Revoke only the access token
accessToken.revoke('access_token')
.then(() => {
// Revoke the refresh token
return accessToken.revoke('refresh_token');
})
.then(() => {
console.log('Token revoked');
})
.catch((error) => {
console.log('Error revoking token.', error.message);
});
// Revoke the refresh token
await accessToken.revoke('refresh_token');
} catch (error) {
console.log('Error revoking token: ', error.message);
}
```

@@ -335,34 +279,33 @@

Exceptions are raised when a 4xx or 5xx status code is returned.
Errors are returned when a 4xx or 5xx status code is received.
HTTPError
BoomError
Through the error message attribute you can access the JSON representation
based on HTTP `status` and error `message`.
As a standard [boom](https://github.com/hapijs/boom) error you can access any of the boom error properties. The total amount of information varies according to the generated status code.
```javascript
// Callbacks
oauth2.authorizationCode.getToken({}, (error, token) => {
if (error) {
return console.log(error.message);
}
});
// Promises
oauth2.authorizationCode
.getToken({})
.catch((error) => {
console.log(error.message);
});
try {
await oauth2.authorizationCode.getToken();
} catch(error) {
console.log(error);
}
// => { "status": "401", "message": "Unauthorized" }
// => {
// "statusCode": 401,
// "error": "Unauthorized",
// "message": "invalid password"
// }
```
## Contributing
See [CONTRIBUTING](https://github.com/lelylan/simple-oauth2/blob/master/CONTRIBUTING.md)
## Authors
[Andrea Reginato](http://twitter.com/lelylan)
### Contributors
Special thanks to the following people for submitting patches.

@@ -373,2 +316,3 @@

## Changelog
See [CHANGELOG](https://github.com/lelylan/simple-oauth2/blob/master/CHANGELOG.md)

@@ -375,0 +319,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc