Socket
Socket
Sign inDemoInstall

client-oauth2

Package Overview
Dependencies
49
Maintainers
1
Versions
39
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0 to 0.2.0

456

client-oauth2.js

@@ -0,7 +1,9 @@

/* global define */
(function (root) {
var isBrowser = typeof window === 'object';
var hasBuffer = typeof Buffer === 'function';
var _hasOwnProperty = Object.prototype.hasOwnProperty;
var btoa = hasBuffer ? btoaBuffer : root.btoa;
var popsicle = isBrowser ? root.popsicle : require('popsicle');
var hasRequire = typeof require === 'function'
var hasBuffer = typeof Buffer === 'function'
var _hasOwnProperty = Object.prototype.hasOwnProperty
var btoa = hasBuffer ? btoaBuffer : root.btoa
var popsicle = hasRequire ? require('popsicle') : root.popsicle

@@ -63,3 +65,3 @@ /**

].join(' ')
};
}

@@ -75,7 +77,7 @@ /**

for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
var source = arguments[i]
for (var key in source) {
if (_hasOwnProperty.call(source, key)) {
dest[key] = source[key];
dest[key] = source[key]
}

@@ -85,3 +87,3 @@ }

return dest;
return dest
}

@@ -96,3 +98,3 @@

function btoaBuffer (string) {
return new Buffer(string).toString('base64');
return new Buffer(string).toString('base64')
}

@@ -110,7 +112,6 @@

for (var i = 0; i < props.length; i++) {
var prop = props[i];
var prop = props[i]
// Check whether the property is empty.
if (obj[prop] == null) {
throw new TypeError('Expected "' + prop + '" to exist');
throw new TypeError('Expected "' + prop + '" to exist')
}

@@ -128,3 +129,3 @@ }

function omit (source, keys) {
var obj = {};
var obj = {}

@@ -134,7 +135,7 @@ // Iterate over the source object and set properties on a new object.

if (keys.indexOf(key) === -1) {
obj[key] = source[key];
obj[key] = source[key]
}
});
})
return obj;
return obj
}

@@ -151,33 +152,33 @@

function decodeQuery (qs, sep, eq) {
eq = eq || '=';
sep = sep || '&';
qs = qs.split(sep);
eq = eq || '='
sep = sep || '&'
qs = qs.split(sep)
var obj = {};
var maxKeys = 1000;
var len = Math.min(qs.length, maxKeys);
var obj = {}
var maxKeys = 1000
var len = Math.min(qs.length, maxKeys)
for (var i = 0; i < len; i++) {
var key = qs[i].replace(/\+/g, '%20');
var value = '';
var index = key.indexOf(eq);
var key = qs[i].replace(/\+/g, '%20')
var value = ''
var index = key.indexOf(eq)
if (index !== -1) {
value = key.substr(index + 1);
key = key.substr(0, index);
value = key.substr(index + 1)
key = key.substr(0, index)
}
key = decodeURIComponent(key);
value = decodeURIComponent(value);
key = decodeURIComponent(key)
value = decodeURIComponent(value)
if (!_hasOwnProperty.call(obj, key)) {
obj[key] = value;
obj[key] = value
} else if (Array.isArray(obj[key])) {
obj[key].push(value);
obj[key].push(value)
} else {
obj[key] = [obj[key], value];
obj[key] = [obj[key], value]
}
}
return obj;
return obj
}

@@ -194,6 +195,6 @@

data.error ||
data.error_message;
data.error_message
// Return an error instance with the message if it exists.
return message && new Error(message);
return message && new Error(message)
}

@@ -208,11 +209,11 @@

function handleAuthResponse (res) {
var data = res.body;
var err = getAuthError(data);
var data = res.body
var err = getAuthError(data)
// If the response contains an error, reject the refresh token.
if (err) {
return Promise.reject(err);
return Promise.reject(err)
}
return data;
return data
}

@@ -228,6 +229,6 @@

if (!Array.isArray(scopes)) {
return scopes == null ? '' : String(scopes);
return scopes == null ? '' : String(scopes)
}
return scopes.join(' ');
return scopes.join(' ')
}

@@ -243,3 +244,3 @@

function createUri (options, tokenType) {
// Check the required parameters have been set.
// Check the required parameters are set.
expects(options, [

@@ -249,20 +250,41 @@ 'clientId',

'authorizationUri'
]);
])
var clientId = encodeURIComponent(options.clientId);
var redirectUri = encodeURIComponent(options.redirectUri);
var scopes = encodeURIComponent(sanitizeScope(options.scopes));
var uri = options.authorizationUri + '?client_id=' + clientId +
var clientId = encodeURIComponent(options.clientId)
var redirectUri = encodeURIComponent(options.redirectUri)
var scopes = encodeURIComponent(sanitizeScope(options.scopes))
var uri = options.authorizationUri + '?client_id=' + clientId +
'&redirect_uri=' + redirectUri +
'&scope=' + scopes +
'&response_type=' + tokenType;
'&response_type=' + tokenType
if (options.state) {
uri += '&state=' + encodeURIComponent(options.state);
uri += '&state=' + encodeURIComponent(options.state)
}
return uri;
return uri
}
/**
* Create basic auth header.
*
* @param {String} username
* @param {String} password
* @return {String}
*/
function auth (username, password) {
return 'Basic ' + btoa(string(username) + ':' + string(password))
}
/**
* Ensure a value is a string.
*
* @param {String} str
* @return {String}
*/
function string (str) {
return str == null ? '' : String(str)
}
/**
* Construct an object that can handle the multiple OAuth 2.0 flows.

@@ -273,8 +295,9 @@ *

function ClientOAuth2 (options) {
this.options = options;
this.options = options
this.code = new CodeFlow(this);
this.token = new TokenFlow(this);
this.owner = new OwnerFlow(this);
this.credentials = new CredentialsFlow(this);
this.code = new CodeFlow(this)
this.token = new TokenFlow(this)
this.owner = new OwnerFlow(this)
this.credentials = new CredentialsFlow(this)
this.jwt = new JwtBearerFlow(this)
}

@@ -287,3 +310,3 @@

*/
ClientOAuth2.Token = ClientOAuth2Token;
ClientOAuth2.Token = ClientOAuth2Token

@@ -301,9 +324,9 @@ /**

data = assign({}, data, {
access_token: access,
access_token: access,
refresh_token: refresh,
token_type: type
});
token_type: type
})
return new ClientOAuth2Token(this, data);
};
return new ClientOAuth2Token(this, data)
}

@@ -321,10 +344,10 @@ /**

if (res.status < 200 || res.status >= 300) {
var err = new Error('HTTP status ' + res.status);
err.status = res.status;
return Promise.reject(err);
var err = new Error('HTTP status ' + res.status)
err.status = res.status
return Promise.reject(err)
}
return res;
});
};
return res
})
}

@@ -334,3 +357,3 @@ /**

*/
ClientOAuth2.prototype.request = popsicle;
ClientOAuth2.prototype.request = popsicle

@@ -344,3 +367,3 @@ /**

function ClientOAuth2Token (client, data) {
this.client = client;
this.client = client

@@ -350,14 +373,26 @@ this.data = omit(data, [

'state', 'error', 'error_description', 'error_uri'
]);
])
this.tokenType = (data.token_type || 'bearer').toLowerCase();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
this.tokenType = (data.token_type || 'bearer').toLowerCase()
this.accessToken = data.access_token
this.refreshToken = data.refresh_token
// Set the expiration date.
if (data.expires_in) {
this.expires = new Date();
this.expiresIn(data.expires_in)
}
this.expires.setSeconds(this.expires.getSeconds() + data.expires_in);
/**
* Expire after some seconds.
*
* @param {Number} duration
* @return {Date}
*/
ClientOAuth2Token.prototype.expiresIn = function (duration) {
if (!isNaN(duration)) {
this.expires = new Date()
this.expires.setSeconds(this.expires.getSeconds() + duration)
} else {
this.expires = undefined
}
return this.expires
}

@@ -373,26 +408,26 @@

if (!this.accessToken) {
throw new Error('Unable to sign without access token');
throw new Error('Unable to sign without access token')
}
opts.headers = opts.headers || {};
opts.headers = opts.headers || {}
if (this.tokenType === 'bearer') {
opts.headers.Authorization = 'Bearer ' + this.accessToken;
opts.headers.Authorization = 'Bearer ' + this.accessToken
} else {
var parts = opts.uri.split('#');
var token = 'access_token=' + this.accessToken;
var uri = parts[0].replace(/[?&]access_token=[^&#]/, '');
var fragment = parts[1] ? '#' + parts[1] : '';
var parts = opts.uri.split('#')
var token = 'access_token=' + this.accessToken
var uri = parts[0].replace(/[?&]access_token=[^&#]/, '')
var fragment = parts[1] ? '#' + parts[1] : ''
// Prepend the correct query string parameter to the uri.
opts.uri = uri + (uri.indexOf('?') > -1 ? '&' : '?') + token + fragment;
opts.uri = uri + (uri.indexOf('?') > -1 ? '&' : '?') + token + fragment
// Attempt to avoid storing the uri in proxies, since the access token
// is exposed in the query parameters.
opts.headers.Pragma = 'no-store';
opts.headers['Cache-Control'] = 'no-store';
opts.headers.Pragma = 'no-store'
opts.headers['Cache-Control'] = 'no-store'
}
return opts;
};
return opts
}

@@ -406,4 +441,4 @@ /**

ClientOAuth2Token.prototype.request = function (opts) {
return this.client.request(this.sign(opts));
};
return this.client.request(this.sign(opts))
}

@@ -416,11 +451,9 @@ /**

ClientOAuth2Token.prototype.refresh = function () {
var self = this;
var options = this.client.options;
var self = this
var options = this.client.options
if (!this.refreshToken) {
return Promise.reject(new Error('No refresh token set'));
return Promise.reject(new Error('No refresh token set'))
}
var authorization = btoa(options.clientId + ':' + options.clientSecret);
return this.client._request({

@@ -430,9 +463,9 @@ url: options.accessTokenUri,

headers: {
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + authorization
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': auth(options.clientId, options.clientSecret)
},
body: {
refresh_token: this.refreshToken,
grant_type: 'refresh_token'
grant_type: 'refresh_token'
}

@@ -442,10 +475,11 @@ })

.then(function (data) {
// Update stored tokens on the current instance.
self.accessToken = data.access_token;
self.refreshToken = data.refresh_token;
self.accessToken = data.access_token
self.refreshToken = data.refresh_token
return self;
});
};
self.expiresIn(data.expires_in)
return self
})
}
/**

@@ -458,7 +492,7 @@ * Check whether the token has expired.

if (this.expires) {
return Date.now() < this.expires.getTime();
return Date.now() < this.expires.getTime()
}
return false;
};
return false
}

@@ -473,3 +507,3 @@ /**

function OwnerFlow (client) {
this.client = client;
this.client = client
}

@@ -485,5 +519,4 @@

OwnerFlow.prototype.getToken = function (username, password) {
var self = this;
var options = this.client.options;
var authorization = btoa(options.clientId + ':' + options.clientSecret);
var self = this
var options = this.client.options

@@ -494,10 +527,10 @@ return this.client._request({

headers: {
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + authorization
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': auth(options.clientId, options.clientSecret)
},
body: {
scope: sanitizeScope(options.scopes),
username: username,
password: password,
scope: sanitizeScope(options.scopes),
username: username,
password: password,
grant_type: 'password'

@@ -508,5 +541,5 @@ }

.then(function (data) {
return new ClientOAuth2Token(self, data);
});
};
return new ClientOAuth2Token(self.client, data)
})
}

@@ -521,3 +554,3 @@ /**

function TokenFlow (client) {
this.client = client;
this.client = client
}

@@ -532,6 +565,6 @@

TokenFlow.prototype.getUri = function (options) {
options = assign({}, this.client.options, options);
options = assign({}, this.client.options, options)
return createUri(options, 'token');
};
return createUri(options, 'token')
}

@@ -546,12 +579,12 @@ /**

TokenFlow.prototype.getToken = function (uri, state) {
var data = {};
var options = this.client.options;
var data = {}
var options = this.client.options
// Make sure the uri matches our expected redirect uri.
if (uri.substr(0, options.redirectUri.length) !== options.redirectUri) {
return Promise.reject(new Error('Should match redirect uri: ' + uri));
return Promise.reject(new Error('Should match redirect uri: ' + uri))
}
var queryIndex = uri.indexOf('?');
var fragmentIndex = uri.indexOf('#');
var queryIndex = uri.indexOf('?')
var fragmentIndex = uri.indexOf('#')

@@ -561,3 +594,3 @@ // If no query string or fragment exists, we won't be able to parse

if (queryIndex === -1 && fragmentIndex === -1) {
return Promise.reject(new Error('Unable to process uri: ' + uri));
return Promise.reject(new Error('Unable to process uri: ' + uri))
}

@@ -568,6 +601,6 @@

if (queryIndex > -1 && queryIndex < fragmentIndex) {
var endIndex = fragmentIndex === -1 ? uri.length : fragmentIndex;
var query = uri.slice(queryIndex + 1, endIndex);
var endIndex = fragmentIndex === -1 ? uri.length : fragmentIndex
var query = uri.slice(queryIndex + 1, endIndex)
assign(data, decodeQuery(query));
assign(data, decodeQuery(query))
}

@@ -578,12 +611,12 @@

if (fragmentIndex > -1) {
var fragment = uri.substr(fragmentIndex + 1);
var fragment = uri.substr(fragmentIndex + 1)
assign(data, decodeQuery(fragment));
assign(data, decodeQuery(fragment))
}
var err = getAuthError(data);
var err = getAuthError(data)
// Check if the query string was populated with a known error.
if (err) {
return Promise.reject(err);
return Promise.reject(err)
}

@@ -593,8 +626,8 @@

if (state != null && data.state !== state) {
return Promise.reject(new Error('Invalid state: ' + data.state));
return Promise.reject(new Error('Invalid state: ' + data.state))
}
// Initalize a new token and return.
return Promise.resolve(new ClientOAuth2Token(this, data));
};
return Promise.resolve(new ClientOAuth2Token(this.client, data))
}

@@ -609,3 +642,3 @@ /**

function CredentialsFlow (client) {
this.client = client;
this.client = client
}

@@ -620,5 +653,5 @@

CredentialsFlow.prototype.getToken = function (options) {
var self = this;
var self = this
options = assign({}, this.client.options, options);
options = assign({}, this.client.options, options)

@@ -629,7 +662,4 @@ expects(options, [

'accessTokenUri'
]);
])
// Base64 encode the client id and secret for the Authorization header.
var authorization = btoa(options.clientId + ':' + options.clientSecret);
return this.client._request({

@@ -639,8 +669,8 @@ url: options.accessTokenUri,

headers: {
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + authorization
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': auth(options.clientId, options.clientSecret)
},
body: {
scope: sanitizeScope(options.scopes),
scope: sanitizeScope(options.scopes),
grant_type: 'client_credentials'

@@ -651,5 +681,5 @@ }

.then(function (data) {
return new ClientOAuth2Token(self, data);
});
};
return new ClientOAuth2Token(self.client, data)
})
}

@@ -664,3 +694,3 @@ /**

function CodeFlow (client) {
this.client = client;
this.client = client
}

@@ -674,6 +704,6 @@

CodeFlow.prototype.getUri = function (options) {
options = assign({}, this.client.options, options);
options = assign({}, this.client.options, options)
return createUri(options, 'code');
};
return createUri(options, 'code')
}

@@ -689,4 +719,4 @@ /**

CodeFlow.prototype.getToken = function (uri, state) {
var self = this;
var options = this.client.options;
var self = this
var options = this.client.options

@@ -698,26 +728,26 @@ expects(options, [

'accessTokenUri'
]);
])
// Make sure the uri matches our expected redirect uri.
if (uri.substr(0, options.redirectUri.length) !== options.redirectUri) {
return Promise.reject(new Error('Should match redirect uri: ' + uri));
return Promise.reject(new Error('Should match redirect uri: ' + uri))
}
var queryIndex = uri.indexOf('?');
var fragmentIndex = uri.indexOf('#');
var queryIndex = uri.indexOf('?')
var fragmentIndex = uri.indexOf('#')
if (queryIndex === -1) {
return Promise.reject(new Error('Unable to process uri: ' + uri));
return Promise.reject(new Error('Unable to process uri: ' + uri))
}
var endIndex = fragmentIndex === -1 ? uri.length : fragmentIndex;
var data = decodeQuery(uri.slice(queryIndex + 1, endIndex));
var err = getAuthError(data);
var endIndex = fragmentIndex === -1 ? uri.length : fragmentIndex
var data = decodeQuery(uri.slice(queryIndex + 1, endIndex))
var err = getAuthError(data)
if (err) {
return Promise.reject(err);
return Promise.reject(err)
}
if (state && data.state !== state) {
return Promise.reject(new Error('Invalid state:' + data.state));
return Promise.reject(new Error('Invalid state:' + data.state))
}

@@ -727,3 +757,3 @@

if (!data.code) {
return Promise.reject(new Error('Missing code, unable to request token'));
return Promise.reject(new Error('Missing code, unable to request token'))
}

@@ -735,10 +765,10 @@

headers: {
'Accept': 'application/json, application/x-www-form-urlencoded',
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: {
code: data.code,
grant_type: 'authorization_code',
redirect_uri: options.redirectUri,
client_id: options.clientId,
code: data.code,
grant_type: 'authorization_code',
redirect_uri: options.redirectUri,
client_id: options.clientId,
client_secret: options.clientSecret

@@ -749,18 +779,68 @@ }

.then(function (data) {
return new ClientOAuth2Token(self, data);
});
};
return new ClientOAuth2Token(self.client, data)
})
}
/**
* Export the OAuth2 client for multiple environments.
* Support JSON Web Token (JWT) Bearer Token OAuth 2.0 grant.
*
* Reference: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-2.1
*
* @param {ClientOAuth2} client
*/
function JwtBearerFlow (client) {
this.client = client
}
/**
* Request an access token using a JWT token.
*
* @param {string} token A JWT token.
* @param {Object} [options]
* @return {Promise}
*/
JwtBearerFlow.prototype.getToken = function (token, options) {
var self = this
options = assign({}, this.client.options, options)
expects(options, [
'accessTokenUri'
])
var headers = {
'Accept': 'application/json, application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded'
}
// Authentication of the client is optional, as described in Section 3.2.1 of OAuth 2.0 [RFC6749]
if (options.clientId) {
headers['Authorization'] = auth(options.clientId, options.clientSecret)
}
return this.client._request({
url: options.accessTokenUri,
method: 'POST',
headers: headers,
body: {
scope: sanitizeScope(options.scopes),
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: token
}
})
.then(handleAuthResponse)
.then(function (data) {
return new ClientOAuth2Token(self.client, data)
})
}
if (typeof define === 'function' && define.amd) {
define([], function () {
return ClientOAuth2;
});
return ClientOAuth2
})
} else if (typeof exports === 'object') {
module.exports = ClientOAuth2;
module.exports = ClientOAuth2
} else {
root.ClientOAuth2 = ClientOAuth2;
root.ClientOAuth2 = ClientOAuth2
}
})(this);
})(this)
{
"name": "client-oauth2",
"version": "0.1.0",
"version": "0.2.0",
"description": "Straight-forward library for executing OAuth 2.0 flows and making API requests.",
"main": "client-oauth2.js",
"files": [
"client-oauth2.js",
"LICENSE"
],
"scripts": {
"test": "gulp lint && gulp test"
"lint": "standard",
"test-browser": "karma start",
"test-node": "mocha -R spec --bail --require test/support/globals.js",
"test": "npm run lint && npm run test-node && npm run test-browser"
},

@@ -25,10 +32,8 @@ "repository": {

"devDependencies": {
"chai": "^1.9.1",
"chai": "^2.0.0",
"es6-promise": "^2.0.0",
"gulp": "^3.8.6",
"gulp-jshint": "^1.8.4",
"gulp-mocha": "^2.0.0",
"is-travis": "^1.0.0",
"karma": "^0.12.23",
"karma-chrome-launcher": "^0.1.4",
"karma-cli": "0.0.4",
"karma-coverage": "^0.2.6",

@@ -38,10 +43,9 @@ "karma-firefox-launcher": "^0.1.3",

"karma-phantomjs-launcher": "^0.1.4",
"karma-sinon-chai": "^0.2.0",
"karma-sinon-chai": "^0.3.0",
"mocha": "^2.0.1",
"nock": "^0.51.0",
"require-dir": "^0.1.0"
"standard": "^2.6.2"
},
"dependencies": {
"popsicle": "0.0.2"
"popsicle": "^0.3.10"
}
}

@@ -142,2 +142,13 @@ # Client OAuth 2.0

### [JWT as Authorization Grant](https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12#section-2.1)
> A JSON Web Token (JWT) Bearer Token can be used to request an access token when a client wishes to utilize an existing trust relationship, expressed through the semantics of (and digital signature or Message Authentication Code calculated over) the JWT, without a direct user approval step at the authorization server.
```javascript
githubAuth.jwt.getToken('eyJhbGciOiJFUzI1NiJ9.eyJpc3Mi[...omitted for brevity...].J9l-ZhwP[...omitted for brevity...]')
.then(function (user) {
console.log(user); //=> { accessToken: '...', tokenType: 'bearer', ... }
});
```
## License

@@ -144,0 +155,0 @@

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc