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

node-oauth2-server

Package Overview
Dependencies
Maintainers
1
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-oauth2-server - npm Package Compare versions

Comparing version 1.5.3-invalid-token to 2.0.0-beta3

examples/postgresql/index.js

11

Changelog.md

@@ -5,8 +5,7 @@ ## Changelog

### 1.5.2
- Fix expiration checking. (Previously the current time was mistakenly cached up instantiation)
### 3.0 (in progress)
- Huge refactor
- Switch from internal router to exposing explit middleware to be added to individual routes
- Switch all model save* functions to take two params, data and callback
### 1.5.1
- Add repository to package.json
### 1.5.0

@@ -60,2 +59,2 @@ - Add support for non-expiring tokens (set accessTokenLifetime/refreshTokenLifetime = null)

- Use async crypto.randomBytes in token generation
- Refactor structure, break into more files
- Refactor structure, break into more files

@@ -80,3 +80,3 @@ /**

client.query('INSERT INTO oauth_refresh_tokens(refresh_token, client_id, user_id, ' +
'expires) VALUES ($1, $2, $3, $4)', [accessToken, clientId, userId, expires],
'expires) VALUES ($1, $2, $3, $4)', [refreshToken, clientId, userId, expires],
function (err, result) {

@@ -83,0 +83,0 @@ callback(err);

@@ -17,118 +17,115 @@ /**

// Modules
var error = require('./error');
var error = require('./error'),
runner = require('./runner');
var authorise = module.exports = {};
module.exports = Authorise;
/**
* Authorise a request with OAuth2
* This is the function order used by the runner
*
* This is a the top level function that should be directly
* passed into the express callback chain to authorise a request
* against OAuth2
* @type {Array}
*/
var fns = [
getBearerToken,
checkToken
];
/**
* Authorise
*
* @param {Object} req Connect request
* @param {Object} res Connect response
* @param {Function} next Connect next
* @return {Void}
* @param {Object} config Instance of OAuth object
* @param {Object} req
* @param {Object} res
* @param {Function} next
*/
authorise.handle = function (req, res, next) {
// Get token
var oauth = this;
authorise.getBearerToken(req, function (err, bearerToken) {
if (err) return next(err);
function Authorise (config, req, next) {
this.config = config;
this.model = config.model;
this.req = req;
oauth.model.getAccessToken(bearerToken, function (err, token) {
if (err) {
return next(error('server_error', false, err));
}
runner(fns, this, next);
}
authorise.validateAccessToken.call(oauth, token, req, next);
});
});
};
/**
* Validate Access Token
* Get bearer token
*
* Check access token retrieved from storage is valid
* Extract token from request according to RFC6750
*
* @param {Object} token Connect token
* @param {Object} req Connect req
* @param {Function} next Connect next
* @return {Void}
* @param {Function} done
* @this OAuth
*/
authorise.validateAccessToken = function (token, req, next) {
if (!token) {
return next(error('invalid_token', 'The access token provided is invalid.'));
}
function getBearerToken (done) {
var headerToken = this.req.get('Authorization'),
getToken = this.req.query.access_token,
postToken = this.req.body.access_token;
// Check it's valid
if (token.expires !== null && (!token.expires || token.expires < this.now)) {
return next(error('invalid_token', 'The access token provided has expired.'));
}
// Check exactly one method was used
var methodsUsed = (headerToken !== undefined) + (getToken !== undefined) +
(postToken !== undefined);
// Expose params
req.oauth.token = token;
req.user = token.user ? token.user : { id: token.user_id };
if (methodsUsed > 1) {
return done(error('invalid_request',
'Only one method may be used to authenticate at a time (Auth header, ' +
'GET or POST).'));
} else if (methodsUsed === 0) {
return done(error('invalid_request', 'The access token was not found'));
}
next(); // Exit point
};
// Header: http://tools.ietf.org/html/rfc6750#section-2.1
if (headerToken) {
var matches = headerToken.match(/Bearer\s(\S+)/);
/**
* Extract access token from request
*
* Checks exactly one access token had been passed and
* does additional validation for each method of passing
* the token.
* Returns OAuth2 Error if any of the above conditions
* aren't met.
*
* @see OAuth2Server#authorizeRequest
*
* @param {Object} req Connect request
* @return {Object|String} Oauth2Error or The access token
*/
authorise.getBearerToken = function (req, next) {
if (!matches) {
return done(error('invalid_request', 'Malformed auth header'));
}
var headerToken = req.get('Authorization'),
getToken = req.query.access_token,
postToken = req.body.access_token;
headerToken = matches[1];
}
// Check exactly one method was used
var methodsUsed = (headerToken !== undefined) + (getToken !== undefined) +
(postToken !== undefined);
// POST: http://tools.ietf.org/html/rfc6750#section-2.2
if (postToken) {
if (this.req.method === 'GET') {
return done(error('invalid_request',
'Method cannot be GET When putting the token in the body.'));
}
if (methodsUsed > 1) {
return next(error('invalid_request',
'Only one method may be used to authenticate at a time (Auth header, GET or POST).'));
} else if (methodsUsed === 0) {
return next(error('invalid_request', 'The access token was not found'));
}
if (!this.req.is('application/x-www-form-urlencoded')) {
return done(error('invalid_request', 'When putting the token in the ' +
'body, content type must be application/x-www-form-urlencoded.'));
}
}
// Header: http://tools.ietf.org/html/rfc6750#section-2.1
if (headerToken) {
var matches = headerToken.match(/Bearer\s(\S+)/);
this.bearerToken = headerToken || postToken || getToken;
done();
}
if (!matches) {
return next(error('invalid_request', 'Malformed auth header'));
}
/**
* Check token
*
* Check it against model, ensure it's not expired
* @param {Function} done
* @this OAuth
*/
function checkToken (done) {
var self = this;
this.model.getAccessToken(this.bearerToken, function (err, token) {
if (err) return done(error('server_error', false, err));
headerToken = matches[1];
}
if (!token) {
return done(error('invalid_token',
'The access token provided is invalid.'));
}
// POST: http://tools.ietf.org/html/rfc6750#section-2.2
if (postToken) {
if (req.method === 'GET') {
return next(error('invalid_request',
'Method cannot be GET When putting the token in the body.'));
}
if (token.expires !== null &&
(!token.expires || token.expires < new Date())) {
return done(error('invalid_token',
'The access token provided has expired.'));
}
if (!req.is('application/x-www-form-urlencoded')) {
return next(error('invalid_request', 'When putting the token in the body, ' +
'content type must be application/x-www-form-urlencoded.'));
}
}
// Expose params
self.req.oauth = { bearerToken: token };
self.req.user = token.user ? token.user : { id: token.user_id };
return next(null, headerToken || postToken || getToken);
};
done();
});
}

@@ -28,23 +28,23 @@ /**

if (!(this instanceof OAuth2Error)) return new OAuth2Error(error, description, err);
if (!(this instanceof OAuth2Error)) return new OAuth2Error(error, description, err);
switch (error) {
case 'invalid_client':
case 'invalid_grant':
case 'invalid_request':
this.code = 400;
break;
switch (error) {
case 'invalid_client':
case 'invalid_grant':
case 'invalid_request':
this.code = 400;
break;
case 'invalid_token':
this.code = 401;
break;
case 'server_error':
this.code = 503;
break;
default:
this.code = 500;
}
case 'server_error':
this.code = 503;
break;
default:
this.code = 500;
}
this.error = error;
this.error_description = description || error;
this.stack = (err && err.stack) || err;
this.error = error;
this.error_description = description || error;
this.stack = (err && err.stack) || err;
}

@@ -17,8 +17,7 @@ /**

// Required modules
var error = require('./error'),
authorise = require('./authorise'),
token = require('./token');
AuthCodeGrant = require('./authCodeGrant'),
Authorise = require('./authorise'),
Grant = require('./grant');
// Expose
module.exports = OAuth2Server;

@@ -29,107 +28,159 @@

*
* @param {Object|Void} config Configuration object
* @param {Object} config Configuration object
*/
function OAuth2Server (config) {
if (!(this instanceof OAuth2Server)) {
return new OAuth2Server(config);
}
if (!(this instanceof OAuth2Server)) return new OAuth2Server(config);
config = config || {};
config = config || {};
if (!config.model) throw new Error('No model supplied to OAuth2Server');
this.model = config.model;
if (!config.model) throw new Error('No model supplied to OAuth2Server');
this.model = config.model;
this.allow = config.allow || [];
this.grants = config.grants || [];
this.debug = config.debug || false;
this.passthroughErrors = config.passthroughErrors;
this.grants = config.grants || [];
this.debug = config.debug || false;
this.passthroughErrors = config.passthroughErrors;
this.accessTokenLifetime = config.accessTokenLifetime !== undefined ?
config.accessTokenLifetime : 3600;
this.refreshTokenLifetime = config.refreshTokenLifetime !== undefined ?
config.refreshTokenLifetime : 1209600;
this.authCodeLifetime = config.authCodeLifetime || 30;
this.accessTokenLifetime = config.accessTokenLifetime !== undefined ?
config.accessTokenLifetime : 3600;
this.refreshTokenLifetime = config.refreshTokenLifetime !== undefined ?
config.refreshTokenLifetime : 1209600;
this.authCodeLifetime = config.authCodeLifetime || 30;
this.regex = {};
this.regex.clientId = config.clientIdRegex || /^[a-z0-9-_]{3,40}$/i;
this.regex.grantType = new RegExp('^(' + this.grants.join('|') + ')$', 'i');
this.regex = {
clientId: config.clientIdRegex || /^[a-z0-9-_]{3,40}$/i,
grantType: new RegExp('^(' + this.grants.join('|') + ')$', 'i')
};
}
/**
* Authorise incoming requests
* Authorisation Middleware
*
* Provides main OAuth middleware that passes oauth
* authorization/token requests to relevant handlers or,
* if it isn't allowed, passes it on to the internal
* authorization handler
* Returns middleware that will authorise the request using oauth,
* if successful it will allow the request to proceed to the next handler
*
* @return {Function} Main OAuth handling middleware
* @return {Function} middleware
*/
OAuth2Server.prototype.handler = function () {
var allowed = this.allow,
allowedIsArray = Array.isArray(allowed),
allowCache = allowedIsArray ? false : {},
oauth = this;
OAuth2Server.prototype.authorise = function () {
var self = this;
return function (req, res, next) {
var method = req.method.toLowerCase(),
allow = allowedIsArray ? allowCache : allowCache[method];
return function (req, res, next) {
new Authorise(self, req, next);
};
};
// Build allow object this method if haven't yet already
if (!allow) {
var paths = allowedIsArray ? allowed :
Array.prototype.concat(allowed.all || [], allowed[method] || []);
/**
* Grant Middleware
*
* Returns middleware that will grant tokens to valid requests.
* This would normally be mounted at '/oauth/token' e.g.
*
* `app.all('/oauth/token', oauth.grant());`
*
* @return {Function} middleware
*/
OAuth2Server.prototype.grant = function () {
var self = this;
allow = {
len: paths.length,
regex: new RegExp('^(' + paths.join('|') + ')$')
};
return function (req, res, next) {
new Grant(self, req, res, next);
};
};
if (allowedIsArray) {
allowCache = allow;
} else {
allowCache[method] = allow;
}
}
/**
* Code Auth Grant Middleware
*
* @param {Function} check Function will be called with req to check if the
* user has authorised the request.
* @return {Function} middleware
*/
OAuth2Server.prototype.authCodeGrant = function (check) {
var self = this;
// Setup request params
req.oauth = { internal: false };
oauth.now = new Date();
if (req.path === '/oauth/token') {
req.oauth.internal = true;
return token.handle.apply(oauth, arguments);
} else if (!allow.len || !req.path.match(allow.regex)) {
return authorise.handle.apply(oauth, arguments);
} else {
return next();
}
};
return function (req, res, next) {
new AuthCodeGrant(self, req, res, next, check);
};
};
/**
* Error Handler
* OAuth Error Middleware
*
* Provides OAuth error handling middleware to catch any errors
* and ensure an oauth complient response
* Returns middleware that will catch OAuth errors and ensure an OAuth
* complaint response
*
* @return {Function} OAuth error handling middleware
* @return {Function} middleware
*/
OAuth2Server.prototype.errorHandler = function () {
var oauth = this;
var self = this;
return function (err, req, res, next) {
if (err instanceof Error && err.status && err.status === 400) {
err = error('invalid_request', err.toString(), err);
} else if (!(err instanceof error)) {
err = error('server_error', false, err);
}
return function (err, req, res, next) {
if (!(err instanceof error) || self.passthroughErrors) return next(err);
if (oauth.debug) console.log(err.stack || err);
if (oauth.passthroughErrors && !req.oauth.internal) return next(err);
if (self.debug) console.log(err.stack || err);
delete err.stack;
res.send(err.code, err);
};
delete err.stack;
res.send(err.code, err);
};
};
/**
* Lockdown
*
* When using the lockdown patter, this function should be called after
* all routes have been declared.
* It will search through each route and if it has not been explitly bypassed
* (by passing oauth.bypass) then authorise will be inserted.
* If oauth.grant has been passed it will replace it with the proper grant
* middleware
* NOTE: When using this method, you must PASS the method not CALL the method,
* e.g.:
*
* `
* app.all('/oauth/token', app.oauth.grant);
*
* app.get('/secrets', function (req, res) {
* res.send('secrets');
* });
*
* app.get('/public', app.oauth.bypass, function (req, res) {
* res.send('publci');
* });
*
* app.oauth.lockdown(app);
* `
*
* @param {Object} app Express app
*/
OAuth2Server.prototype.lockdown = function (app) {
var self = this;
var lockdown = function (route) {
// Check if it's a grant route
var pos = route.callbacks.indexOf(self.grant);
if (pos !== -1) {
route.callbacks[pos] = self.grant();
return;
}
// Check it's not been explitly bypassed
pos = route.callbacks.indexOf(self.bypass);
if (pos === -1) {
route.callbacks.unshift(self.authorise());
} else {
route.callbacks.splice(pos, 1);
}
};
for (var method in app.routes) {
app.routes[method].forEach(lockdown);
}
};
/**
* Bypass
*
* This is used as placeholder for when using the lockdown pattern
*
* @return {Function} noop
*/
OAuth2Server.prototype.bypass = function () {};

@@ -16,319 +16,43 @@ /**

*/
// Modules
var crypto = require('crypto'),
error = require('./error');
error = require('./error');
var token = module.exports = {};
module.exports = Token;
/**
* Token endpoint
* Token generator that will delegate to model or
* the internal random generator
*
* @param {Object} req Connect request
* @param {Object} res Connect response
* @param {Function} next Connect next
* @return {Void}
* @param {String} type 'accessToken' or 'refreshToken'
* @param {Function} callback
*/
token.handle = function (req, res, next) {
// Only POST via application/x-www-form-urlencoded is acceptable
if (req.method !== 'POST' || !req.is('application/x-www-form-urlencoded')) {
return next(error('invalid_request',
'Method must be POST with application/x-www-form-urlencoded encoding'));
}
function Token (config, type, callback) {
if (config.model.generateToken) {
config.model.generateToken(type, config.req, function (err, token) {
if (err) return callback(error('server_error', false, err));
if (!token) return generateRandomToken(callback);
callback(false, token);
});
} else {
generateRandomToken(callback);
}
}
// Grant type
req.oauth.grantType = req.body && req.body.grant_type;
if (!req.oauth.grantType || !req.oauth.grantType.match(this.regex.grantType)) {
return next(error('invalid_request', 'Invalid or missing grant_type parameter'));
}
// Extract credentials
// http://tools.ietf.org/html/rfc6749#section-3.2.1
var creds = token.getClientCredentials(req);
if (!creds.client_id || !creds.client_id.match(this.regex.clientId)) {
return next(error('invalid_client', 'Invalid or missing client_id parameter'));
} else if (!creds.client_secret) {
return next(error('invalid_client', 'Missing client_secret parameter'));
}
// Check credentials against model
var oauth = this;
this.model.getClient(creds.client_id, creds.client_secret, function (err, client) {
if (err) {
return next(error('server_error', false, err));
}
if (!client) {
return next(error('invalid_client', 'The client credentials are invalid'));
}
req.oauth.client = client;
oauth.model.grantTypeAllowed(client.client_id, req.oauth.grantType, function (err, allowed) {
if (!allowed) {
return next(error('invalid_client',
'The grant type is unauthorised for this client_id'));
}
token.grant.call(oauth, req, res, next);
});
});
};
/**
* Convinience function for extracting client credentials from request
* Internal random token generator
*
* @see OAuth2Server#token
*
* @param {Object} req Connect request
* @return {Object} Client id/secret from headers or body
* @param {Function} callback
*/
token.getClientCredentials = function (req) {
var generateRandomToken = function (callback) {
crypto.randomBytes(256, function (ex, buffer) {
if (ex) return callback(error('server_error'));
// Return object
var creds = function (clientId, clientSecret) {
this.client_id = clientId;
this.client_secret = clientSecret;
};
var token = crypto
.createHash('sha1')
.update(buffer)
.digest('hex');
// Check for Basic Auth
// Pulled from Connect:
// https://github.com/senchalabs/connect/blob/master/lib/middleware/basicAuth.js#L65
var fromBasicAuth = function () {
var authorization = req.get('authorization');
if (!authorization) return false;
var parts = authorization.split(' ');
if (parts.length !== 2) return false;
var scheme = parts[0],
credentials = new Buffer(parts[1], 'base64').toString().replace(/^\s+|\s+$/g, ""),
index = credentials.indexOf(':');
if (scheme !== 'Basic' || index < 0) return false;
return new creds(credentials.slice(0, index), credentials.slice(index + 1));
};
return fromBasicAuth() || new creds(req.body.client_id, req.body.client_secret);
callback(false, token);
});
};
/**
* Grant access token based on grant_type
*
* @see OAuth2Server#token
*
* @param {Object} req Connect request
* @param {Object} res Connect response
* @param {Function} next Connect next
* @return {Void}
*/
token.grant = function (req, res, next) {
var invalid = function () {
next(error('invalid_request', 'Invalid grant_type parameter or parameter missing'));
};
var oauth = this;
if (req.oauth.grantType.match(/^http(s|):\/\//) && this.model.extendedGrant) {
return this.model.extendedGrant(req, function (err, supported, user) {
if (err && err.error && err.description) {
return next(error(err.error, err.description));
}
if (err) return next(err);
if (!supported) return invalid();
if (!user || user.id === undefined) {
return next(error('invalid_request', 'Invalid request.'));
}
req.user = user;
token.grantAccessToken.call(oauth, req, res, next);
});
}
switch (req.oauth.grantType) {
case 'password':
// User credentials
var uname = req.body.username,
pword = req.body.password;
if (!uname || !pword) {
return next(error('invalid_client',
'Missing parameters. "username" and "password" are required'));
}
return this.model.getUser(uname, pword, function (err, user) {
if (err) {
return next(error('server_error', false, err));
}
if (user) {
req.user = user;
token.grantAccessToken.call(oauth, req, res, next);
} else {
next(error('invalid_grant', 'User credentials are invalid'));
}
});
case 'refresh_token':
if (!req.body.refresh_token) {
return next(error('invalid_request', 'No "refresh_token" parameter'));
}
return this.model.getRefreshToken(req.body.refresh_token, function (err, refreshToken) {
if (err) return next(error('server_error', false, err));
if (!refreshToken || refreshToken.client_id !== req.oauth.client.client_id) {
return next(error('invalid_grant', 'Invalid refresh token'));
} else if (refreshToken.expires !== null && refreshToken.expires < oauth.now) {
return next(error('invalid_grant', 'Refresh token has expired'));
}
if (refreshToken.user_id) {
req.user = { id: refreshToken.user_id };
} else {
return next(error('server_error', false,
'No user/user_id parameter returned from getRefreshToken'));
}
if (oauth.model.revokeRefreshToken) {
oauth.model.revokeRefreshToken(req.body.refresh_token, function (err) {
if (err) return next(error('server_error', false, err));
token.grantAccessToken.call(oauth, req, res, next);
});
} else {
return token.grantAccessToken.call(oauth, req, res, next);
}
});
default:
return invalid();
}
};
/**
* Save access token ready for issuing
*
* @see OAuth2Server#grant
*
* @param {Object} req Connect request
* @param {Object} res Connect response
* @param {Function} next Connect next
* @return {Void}
*/
token.grantAccessToken = function (req, res, next) {
var oauth = this;
var createRefreshToken = function (err, refreshToken) {
if (err || !refreshToken) return next(err);
// Object indicates a reissue
if (typeof refreshToken === 'object' && refreshToken.refresh_token) {
req.oauth.accessToken.refresh_token = { refresh_token: refreshToken.refresh_token };
return token.issueToken.call(oauth, req, res, next);
}
req.oauth.accessToken.refresh_token = refreshToken;
var expires = null;
if (oauth.refreshTokenLifetime !== null) {
expires = new Date(oauth.now);
expires.setSeconds(expires.getSeconds() + oauth.refreshTokenLifetime);
}
oauth.model.saveRefreshToken(req.oauth.accessToken.refresh_token,
req.oauth.client.client_id, req.user.id, expires, function (err) {
if (err) return next(error('server_error', false, err));
token.issueToken.call(oauth, req, res, next);
});
};
var issueRefreshToken = function () {
// Are we issuing refresh tokens?
if (oauth.grants.indexOf('refresh_token') >= 0) {
token.generateToken.call(oauth, 'refreshToken', req, createRefreshToken);
} else {
token.issueToken.call(oauth, req, res, next);
}
};
var createAccessToken = function (err, accessToken) {
if (err || !accessToken) return next(err);
// Object idicates a reissue
if (typeof accessToken === 'object' && accessToken.access_token) {
req.oauth.accessToken = { access_token: accessToken.access_token };
return issueRefreshToken();
}
req.oauth.accessToken = { access_token: accessToken };
var expires = null;
if (oauth.accessTokenLifetime !== null) {
expires = new Date(oauth.now);
expires.setSeconds(expires.getSeconds() + oauth.accessTokenLifetime);
}
oauth.model.saveAccessToken(req.oauth.accessToken.access_token, req.oauth.client.client_id,
req.user.id, expires, function (err) {
if (err) return next(error('server_error', false, err));
issueRefreshToken();
});
};
token.generateToken.call(oauth, 'accessToken', req, createAccessToken);
};
/**
* Actually issue the token and send the response
*
* @see OAuth2Server#grantAccessToken
*
* @param {Object} req Connect request
* @param {Object} res Connect response
* @param {Function} next Connect next
* @return {Void}
*/
token.issueToken = function (req, res, next) {
// Prepare for output
req.oauth.accessToken.token_type = 'bearer';
if (this.accessTokenLifetime !== null) {
req.oauth.accessToken.expires_in = this.accessTokenLifetime;
}
// That's it!
res.set({ 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' });
res.jsonp(req.oauth.accessToken);
};
/**
* Convinience function for generating a token
*
* @param {String} req Connect req
* @param {Function} next Connect next
* @param {Function} callback
* @return {Void}
*/
token.generateToken = function (type, req, callback) {
if (this.model.generateToken) {
this.model.generateToken(type, req, function (err, generatedToken) {
if (err) return callback(error('server_error', false, err));
if (!generatedToken) return token._generateToken(callback);
callback(false, generatedToken);
});
} else {
token._generateToken(callback);
}
};
token._generateToken= function (callback) {
crypto.randomBytes(256, function (ex, buffer) {
if (ex) return callback(error('server_error'));
callback(false, crypto.createHash('sha1').update(buffer).digest('hex'));
});
};
{
"name": "node-oauth2-server",
"description": "Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with express in node.js",
"version": "1.5.3-invalid-token",
"keywords": [
"oauth",
"oauth2"
],
"author": {
"name": "NightWorld",
"email": "code@nightworld.com"
},
"contributors": [
{
"name": "Thom Seddon",
"email": "thom@nightworld.com"
}
],
"main": "lib/oauth2server.js",
"dependencies": {},
"devDependencies": {
"express": "3.1.x",
"mocha": "1.8.x",
"should": "1.2.x",
"supertest": "0.5.x"
},
"licenses": [
{
"type": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
],
"engines": {
"node": ">=0.8"
},
"scripts": {
"test": "mocha"
},
"repository": {
"type": "git",
"url": "https://github.com/nightworld/node-oauth2-server.git"
}
"name": "node-oauth2-server",
"description": "Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with express in node.js",
"version": "2.0.0-beta3",
"keywords": [
"oauth",
"oauth2"
],
"author": {
"name": "NightWorld",
"email": "code@nightworld.com"
},
"contributors": [
{
"name": "Thom Seddon",
"email": "thom@nightworld.com"
}
],
"main": "lib/oauth2server.js",
"dependencies": {},
"devDependencies": {
"express": "3.1.x",
"mocha": "1.8.x",
"should": "1.2.x",
"supertest": "0.5.x"
},
"licenses": [
{
"type": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
],
"engines": {
"node": ">=0.8"
},
"scripts": {
"test": "mocha"
},
"repository": {
"type": "git",
"url": "https://github.com/nightworld/node-oauth2-server.git"
}
}

@@ -5,8 +5,5 @@ # Node OAuth2 Server [![Build Status](https://travis-ci.org/nightworld/node-oauth2-server.png?branch=master)](https://travis-ci.org/nightworld/node-oauth2-server)

## 2.0
Version is under active development, for a preview see the 2.0 branch: https://github.com/nightworld/node-oauth2-server/tree/2.0
## Installation
$ npm install node-oauth2-server
$ npm install node-oauth2-server

@@ -19,3 +16,3 @@ ## Quick Start

var express = require('express'),
oauthserver = require('node-oauth2-server');
oauthserver = require('node-oauth2-server');

@@ -25,16 +22,18 @@ var app = express();

app.configure(function() {
var oauth = oauthserver({
model: {}, // See below for specification
grants: ['password'],
debug: true
});
app.use(express.bodyParser()); // REQUIRED
app.use(oauth.handler());
app.use(oauth.errorHandler());
app.oauth = oauthserver({
model: {}, // See below for specification
grants: ['password'],
debug: true
});
app.use(express.bodyParser()); // REQUIRED
});
app.get('/', function (req, res) {
res.send('Secret area');
app.all('/oauth/token', app.oauth.grant());
app.get('/', app.oauth.authorise(), function (req, res) {
res.send('Secret area');
});
app.use(app.oauth.errorHandler());
app.listen(3000);

@@ -49,10 +48,6 @@ ```

- Supports password, refresh_token and extension (custom) grant types
- Supports authorization_code, password, refresh_token and extension (custom) grant types
- Implicitly supports any form of storage e.g. PostgreSQL, MySQL, Mongo, Redis...
- Full test suite
## Limitations
- Does not yet support authorization code grant type
## Options

@@ -62,7 +57,2 @@

- Model object (see below)
- *array|object* **allow**
- Paths to allow to bypass authorisation, can take either form:
- array, all methods allowed: `['/path1', '/path2']`
- object or arrays keyed by method: `{ get: ['/path1'], post: ['/path2'], all: ['/path3'] }`
- Default: `[]`
- *array* **grants**

@@ -74,4 +64,2 @@ - grant types you wish to support, currently the module supports `password` and `refresh_token`

- Default: `false`
- *boolean* **passthroughErrors**
- If true, **non grant** errors will not be handled internally (so you can ensure a consistent format with the rest of your api)
- Default: `false`

@@ -92,2 +80,4 @@ - *number* **accessTokenLifetime**

- Default: `/^[a-z0-9-_]{3,40}$/i`
- *boolean* **passthroughErrors**
- If true, **non grant** errors will not be handled internally (so you can ensure a consistent format with the rest of your api)

@@ -139,7 +129,8 @@ ## Model Specification

#### saveAccessToken (accessToken, clientId, userId, expires, callback)
- *string* **accessToken**
- *string* **clientId**
- *string|number* **userId**
- *date* **expires**
#### saveAccessToken (accessToken, callback)
- *object* **accessToken**
- *string* **accessToken**
- *string* **clientId**
- *string|number* **userId**
- *date* **expires**
- *function* **callback (error)**

@@ -150,2 +141,31 @@ - *mixed* **error**

### Required for `authorization_code` grant type
#### getAuthCode (authCode, callback)
- *string* **authCode**
- *function* **callback (error, authCode)**
- *mixed* **error**
- Truthy to indicate an error
- *object* **authCode**
- The authorization code retrieved form storage or falsey to indicate invalid code
- Must contain the following keys:
- *string|number* **client_id**
- client_id associated with this auth code
- *date* **expires**
- The date when it expires
- *string|number* **user_id**
- The user_id
#### saveAuthCode (authCode, callback)
- *object* **authCode**
- *string* **auth_code**
- *string* **client_id**
- *date* **expires**
- *mixed* **user**
- Whatever was passed as `user` to the codeGrant function (see example)
- *function* **callback (error)**
- *mixed* **error**
- Truthy to indicate an error
### Required for `password` grant type

@@ -167,7 +187,8 @@

#### saveRefreshToken (refreshToken, clientId, userId, expires, callback)
- *string* **refreshToken**
- *string* **clientId**
- *string|number* **userId**
- *date* **expires**
#### saveRefreshToken (refreshToken, callback)
- *object* **refreshToken**
- *string* **refreshToken**
- *string* **clientId**
- *string|number* **userId**
- *date* **expires**
- *function* **callback (error)**

@@ -272,6 +293,6 @@ - *mixed* **error**

{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}

@@ -278,0 +299,0 @@ ```

@@ -17,6 +17,5 @@ /**

var assert = require('assert'),
express = require('express'),
request = require('supertest'),
should = require('should');
var express = require('express'),
request = require('supertest'),
should = require('should');

@@ -26,62 +25,66 @@ var oauth2server = require('../');

var bootstrap = function (oauthConfig) {
var app = express(),
oauth = oauth2server(oauthConfig || { model: {} });
var app = express(),
oauth = oauth2server(oauthConfig || { model: {} });
app.use(express.bodyParser());
app.use(oauth.handler());
app.use(oauth.errorHandler());
app.use(express.bodyParser());
if (oauthConfig && oauthConfig.passthroughErrors) {
app.use(function (err, req, res, next) {
res.send('passthrough');
});
}
app.all('/oauth/token', oauth.grant());
app.all('/', oauth.authorise(), function (req, res) {
res.send('Hello World');
});
return app;
app.use(oauth.errorHandler());
if (oauthConfig && oauthConfig.passthroughErrors) {
app.use(function (err, req, res, next) {
res.send('passthrough');
});
}
return app;
};
describe('OAuth2Server.errorHandler()', function() {
it('should return an oauth conformat response', function (done) {
var app = bootstrap();
describe('Error Handler', function() {
it('should return an oauth conformat response', function (done) {
var app = bootstrap();
request(app)
.get('/')
.expect(400)
.end(function (err, res) {
if (err) return done(err);
request(app)
.get('/')
.expect(400)
.end(function (err, res) {
if (err) return done(err);
res.body.should.have.keys('code', 'error', 'error_description');
res.body.should.have.keys('code', 'error', 'error_description');
res.body.code.should.be.a('number');
res.body.code.should.equal(res.statusCode);
res.body.code.should.be.a('number');
res.body.code.should.equal(res.statusCode);
res.body.error.should.be.a('string');
res.body.error.should.be.a('string');
res.body.error_description.should.be.a('string');
res.body.error_description.should.be.a('string');
done();
});
});
done();
});
});
it('should passthrough non grant errors if requested', function (done) {
var app = bootstrap({
passthroughErrors: true,
model: {}
});
it('should passthrough authorise errors', function (done) {
var app = bootstrap({
passthroughErrors: true,
model: {}
});
request(app)
.get('/')
.expect(200, /^passthrough$/, done);
});
request(app)
.get('/')
.expect(200, /^passthrough$/, done);
});
it('should never passthrough grant errors', function (done) {
var app = bootstrap({
passthroughErrors: true,
model: {}
});
it('should passthrough grant errors', function (done) {
var app = bootstrap({
passthroughErrors: true,
model: {}
});
request(app)
.post('/oauth/token')
.expect(400, done);
});
request(app)
.post('/oauth/token')
.expect(200, /^passthrough$/, done);
});
});
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