oauth2orize
Advanced tools
Comparing version 0.1.0 to 1.0.0
@@ -9,5 +9,7 @@ /** | ||
switch (code) { | ||
case 'invalid_client': status = 401; break; | ||
case 'invalid_request': status = 400; break; | ||
case 'unauthorized_client': status = 403; break; | ||
case 'access_denied': status = 403; break; | ||
case 'server_error': status = 500; break; | ||
case 'unsupported_response_type': status = 501; break; | ||
case 'invalid_scope': status = 400; break; | ||
case 'temporarily_unavailable': status = 503; break; | ||
@@ -20,7 +22,7 @@ } | ||
this.name = 'AuthorizationError'; | ||
this.message = message || null; | ||
this.message = message; | ||
this.code = code || 'server_error'; | ||
this.uri = uri; | ||
this.status = status || 400; | ||
}; | ||
this.status = status || 500; | ||
} | ||
@@ -27,0 +29,0 @@ /** |
@@ -10,4 +10,5 @@ /** | ||
this.name = 'BadRequestError'; | ||
this.message = message || null; | ||
}; | ||
this.message = message; | ||
this.status = 400; | ||
} | ||
@@ -14,0 +15,0 @@ /** |
@@ -5,3 +5,3 @@ /** | ||
var utils = require('../utils') | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
, TokenError = require('../errors/tokenerror'); | ||
@@ -59,10 +59,10 @@ | ||
*/ | ||
module.exports = function authorizationCode(options, issue) { | ||
module.exports = function(options, issue) { | ||
if (typeof options == 'function') { | ||
issue = options; | ||
options = null; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
if (!issue) throw new Error('OAuth 2.0 authorizationCode exchange middleware requires an issue function.'); | ||
if (!issue) { throw new TypeError('oauth2orize.authorizationCode exchange requires an issue callback'); } | ||
@@ -72,3 +72,3 @@ var userProperty = options.userProperty || 'user'; | ||
return function authorization_code(req, res, next) { | ||
if (!req.body) { return next(new Error('Request body not parsed. Use bodyParser middleware.')); } | ||
if (!req.body) { return next(new Error('OAuth2orize requires body parsing. Did you forget app.use(express.bodyParser())?')); } | ||
@@ -78,24 +78,32 @@ // The 'user' property of `req` holds the authenticated user. In the case | ||
var client = req[userProperty] | ||
, code = req.body['code'] | ||
, redirectURI = req.body['redirect_uri']; | ||
, code = req.body.code | ||
, redirectURI = req.body.redirect_uri; | ||
if (!code) { return next(new AuthorizationError('missing code parameter', 'invalid_request')); } | ||
if (!code) { return next(new TokenError('Missing required parameter: code', 'invalid_request')); } | ||
issue(client, code, redirectURI, function(err, accessToken, refreshToken, params) { | ||
if (err) { return next(err); } | ||
if (!accessToken) { return next(new AuthorizationError('invalid code', 'invalid_grant')); } | ||
try { | ||
issue(client, code, redirectURI, function(err, accessToken, refreshToken, params) { | ||
if (err) { return next(err); } | ||
if (!accessToken) { return next(new TokenError('Invalid authorization code', 'invalid_grant')); } | ||
if (refreshToken && typeof refreshToken == 'object') { | ||
params = refreshToken; | ||
refreshToken = null; | ||
} | ||
var tok = {}; | ||
tok['access_token'] = accessToken; | ||
if (refreshToken) { tok['refresh_token'] = refreshToken; } | ||
if (params) { utils.merge(tok, params); } | ||
tok['token_type'] = tok['token_type'] || 'bearer'; | ||
var tok = {}; | ||
tok.access_token = accessToken; | ||
if (refreshToken) { tok.refresh_token = refreshToken; } | ||
if (params) { utils.merge(tok, params); } | ||
tok.token_type = tok.token_type || 'Bearer'; | ||
var json = JSON.stringify(tok); | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.setHeader('Cache-Control', 'no-store'); | ||
res.setHeader('Pragma', 'no-cache'); | ||
res.end(json); | ||
}); | ||
} | ||
} | ||
var json = JSON.stringify(tok); | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.setHeader('Cache-Control', 'no-store'); | ||
res.setHeader('Pragma', 'no-cache'); | ||
res.end(json); | ||
}); | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
}; | ||
}; |
@@ -5,3 +5,3 @@ /** | ||
var utils = require('../utils') | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
, TokenError = require('../errors/tokenerror'); | ||
@@ -26,9 +26,10 @@ | ||
* | ||
* done(err, accessToken, refreshToken, params) | ||
* done(err, accessToken, [refreshToken], [params]) | ||
* | ||
* `accessToken` is the access token that will be sent to the client. An | ||
* optional `refreshToken` will be sent to the client, if the server chooses to | ||
* implement support for this functionality. Any additional `params` will be | ||
* included in the response. If an error occurs, `done` should be invoked with | ||
* `err` set in idomatic Node.js fashion. | ||
* implement support for this functionality (note that the spec says a refresh | ||
* token should not be included). Any additional `params` will be included in | ||
* the response. If an error occurs, `done` should be invoked with `err` set in | ||
* idomatic Node.js fashion. | ||
* | ||
@@ -58,10 +59,10 @@ * Options: | ||
*/ | ||
module.exports = function clientCredentials(options, issue) { | ||
module.exports = function(options, issue) { | ||
if (typeof options == 'function') { | ||
issue = options; | ||
options = null; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
if (!issue) throw new Error('OAuth 2.0 clientCredentials exchange middleware requires an issue function.'); | ||
if (!issue) { throw new TypeError('oauth2orize.clientCredentials exchange requires an issue callback'); } | ||
@@ -81,3 +82,3 @@ var userProperty = options.userProperty || 'user'; | ||
return function client_credentials(req, res, next) { | ||
if (!req.body) { return next(new Error('Request body not parsed. Use bodyParser middleware.')); } | ||
if (!req.body) { return next(new Error('OAuth2orize requires body parsing. Did you forget app.use(express.bodyParser())?')); } | ||
@@ -87,3 +88,3 @@ // The 'user' property of `req` holds the authenticated user. In the case | ||
var client = req[userProperty] | ||
, scope = req.body['scope']; | ||
, scope = req.body.scope; | ||
@@ -105,9 +106,13 @@ if (scope) { | ||
if (err) { return next(err); } | ||
if (!accessToken) { return next(new AuthorizationError('invalid client credentials', 'invalid_grant')); } | ||
if (!accessToken) { return next(new TokenError('Invalid client credentials', 'invalid_grant')); } | ||
if (refreshToken && typeof refreshToken == 'object') { | ||
params = refreshToken; | ||
refreshToken = null; | ||
} | ||
var tok = {}; | ||
tok['access_token'] = accessToken; | ||
if (refreshToken) { tok['refresh_token'] = refreshToken; } | ||
tok.access_token = accessToken; | ||
if (refreshToken) { tok.refresh_token = refreshToken; } | ||
if (params) { utils.merge(tok, params); } | ||
tok['token_type'] = tok['token_type'] || 'bearer'; | ||
tok.token_type = tok.token_type || 'Bearer'; | ||
@@ -121,9 +126,13 @@ var json = JSON.stringify(tok); | ||
var arity = issue.length; | ||
if (arity == 3) { | ||
issue(client, scope, issued); | ||
} else { // arity == 2 | ||
issue(client, issued); | ||
try { | ||
var arity = issue.length; | ||
if (arity == 3) { | ||
issue(client, scope, issued); | ||
} else { // arity == 2 | ||
issue(client, issued); | ||
} | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
} | ||
} | ||
}; | ||
}; |
@@ -5,3 +5,3 @@ /** | ||
var utils = require('../utils') | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
, TokenError = require('../errors/tokenerror'); | ||
@@ -59,10 +59,10 @@ | ||
*/ | ||
module.exports = function password(options, issue) { | ||
module.exports = function(options, issue) { | ||
if (typeof options == 'function') { | ||
issue = options; | ||
options = null; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
if (!issue) throw new Error('OAuth 2.0 password exchange middleware requires an issue function.'); | ||
if (!issue) { throw new TypeError('oauth2orize.password exchange requires an issue callback'); } | ||
@@ -82,3 +82,3 @@ var userProperty = options.userProperty || 'user'; | ||
return function password(req, res, next) { | ||
if (!req.body) { return next(new Error('Request body not parsed. Use bodyParser middleware.')); } | ||
if (!req.body) { return next(new Error('OAuth2orize requires body parsing. Did you forget app.use(express.bodyParser())?')); } | ||
@@ -88,8 +88,8 @@ // The 'user' property of `req` holds the authenticated user. In the case | ||
var client = req[userProperty] | ||
, username = req.body['username'] | ||
, password = req.body['password'] | ||
, scope = req.body['scope']; | ||
, username = req.body.username | ||
, passwd = req.body.password | ||
, scope = req.body.scope; | ||
if (!username) { return next(new AuthorizationError('missing username parameter', 'invalid_request')); } | ||
if (!password) { return next(new AuthorizationError('missing password parameter', 'invalid_request')); } | ||
if (!username) { return next(new TokenError('Missing required parameter: username', 'invalid_request')); } | ||
if (!passwd) { return next(new TokenError('Missing required parameter: password', 'invalid_request')); } | ||
@@ -111,9 +111,13 @@ if (scope) { | ||
if (err) { return next(err); } | ||
if (!accessToken) { return next(new AuthorizationError('invalid resource owner credentials', 'invalid_grant')); } | ||
if (!accessToken) { return next(new TokenError('Invalid resource owner credentials', 'invalid_grant')); } | ||
if (refreshToken && typeof refreshToken == 'object') { | ||
params = refreshToken; | ||
refreshToken = null; | ||
} | ||
var tok = {}; | ||
tok['access_token'] = accessToken; | ||
if (refreshToken) { tok['refresh_token'] = refreshToken; } | ||
tok.access_token = accessToken; | ||
if (refreshToken) { tok.refresh_token = refreshToken; } | ||
if (params) { utils.merge(tok, params); } | ||
tok['token_type'] = tok['token_type'] || 'bearer'; | ||
tok.token_type = tok.token_type || 'Bearer'; | ||
@@ -127,9 +131,13 @@ var json = JSON.stringify(tok); | ||
var arity = issue.length; | ||
if (arity == 5) { | ||
issue(client, username, password, scope, issued); | ||
} else { // arity == 4 | ||
issue(client, username, password, issued); | ||
try { | ||
var arity = issue.length; | ||
if (arity == 5) { | ||
issue(client, username, passwd, scope, issued); | ||
} else { // arity == 4 | ||
issue(client, username, passwd, issued); | ||
} | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
} | ||
} | ||
}; | ||
}; |
@@ -5,3 +5,3 @@ /** | ||
var utils = require('../utils') | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
, TokenError = require('../errors/tokenerror'); | ||
@@ -56,10 +56,10 @@ | ||
*/ | ||
module.exports = function refreshToken(options, issue) { | ||
module.exports = function(options, issue) { | ||
if (typeof options == 'function') { | ||
issue = options; | ||
options = null; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
if (!issue) throw new Error('OAuth 2.0 refreshToken exchange middleware requires an issue function.'); | ||
if (!issue) { throw new TypeError('oauth2orize.refreshToken exchange requires an issue callback'); } | ||
@@ -79,3 +79,3 @@ var userProperty = options.userProperty || 'user'; | ||
return function refresh_token(req, res, next) { | ||
if (!req.body) { return next(new Error('Request body not parsed. Use bodyParser middleware.')); } | ||
if (!req.body) { return next(new Error('OAuth2orize requires body parsing. Did you forget app.use(express.bodyParser())?')); } | ||
@@ -85,6 +85,6 @@ // The 'user' property of `req` holds the authenticated user. In the case | ||
var client = req[userProperty] | ||
, refreshToken = req.body['refresh_token'] | ||
, scope = req.body['scope']; | ||
, refreshToken = req.body.refresh_token | ||
, scope = req.body.scope; | ||
if (!refreshToken) { return next(new AuthorizationError('missing refresh_token parameter', 'invalid_request')); } | ||
if (!refreshToken) { return next(new TokenError('Missing required parameter: refresh_token', 'invalid_request')); } | ||
@@ -106,9 +106,13 @@ if (scope) { | ||
if (err) { return next(err); } | ||
if (!accessToken) { return next(new AuthorizationError('invalid refresh token', 'invalid_grant')); } | ||
if (!accessToken) { return next(new TokenError('Invalid refresh token', 'invalid_grant')); } | ||
if (refreshToken && typeof refreshToken == 'object') { | ||
params = refreshToken; | ||
refreshToken = null; | ||
} | ||
var tok = {}; | ||
tok['access_token'] = accessToken; | ||
if (refreshToken) { tok['refresh_token'] = refreshToken; } | ||
tok.access_token = accessToken; | ||
if (refreshToken) { tok.refresh_token = refreshToken; } | ||
if (params) { utils.merge(tok, params); } | ||
tok['token_type'] = tok['token_type'] || 'bearer'; | ||
tok.token_type = tok.token_type || 'Bearer'; | ||
@@ -122,9 +126,13 @@ var json = JSON.stringify(tok); | ||
var arity = issue.length; | ||
if (arity == 4) { | ||
issue(client, refreshToken, scope, issued); | ||
} else { // arity == 3 | ||
issue(client, refreshToken, issued); | ||
try { | ||
var arity = issue.length; | ||
if (arity == 4) { | ||
issue(client, refreshToken, scope, issued); | ||
} else { // arity == 3 | ||
issue(client, refreshToken, issued); | ||
} | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
} | ||
} | ||
}; | ||
}; |
@@ -61,7 +61,7 @@ /** | ||
issue = options; | ||
options = null; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
if (!issue) throw new Error('OAuth 2.0 code grant middleware requires an issue function.'); | ||
if (!issue) { throw new TypeError('oauth2orize.code grant requires an issue callback'); } | ||
@@ -85,8 +85,8 @@ // For maximum flexibility, multiple scope spearators can optionally be | ||
function request(req) { | ||
var clientID = req.query['client_id'] | ||
, redirectURI = req.query['redirect_uri'] | ||
, scope = req.query['scope'] | ||
, state = req.query['state']; | ||
var clientID = req.query.client_id | ||
, redirectURI = req.query.redirect_uri | ||
, scope = req.query.scope | ||
, state = req.query.state; | ||
if (!clientID) { throw new AuthorizationError('missing client_id parameter', 'invalid_request'); } | ||
if (!clientID) { throw new AuthorizationError('Missing required parameter: client_id', 'invalid_request'); } | ||
@@ -112,3 +112,3 @@ if (scope) { | ||
state: state | ||
} | ||
}; | ||
} | ||
@@ -124,8 +124,8 @@ | ||
function response(txn, res, next) { | ||
if (!txn.redirectURI) { return next(new Error('No redirect URI available to send OAuth 2.0 response.')); } | ||
if (!txn.redirectURI) { return next(new Error('Unable to issue redirect for OAuth 2.0 transaction')); } | ||
if (!txn.res.allow) { | ||
var parsed = url.parse(txn.redirectURI, true); | ||
delete parsed.search; | ||
parsed.query['error'] = 'access_denied'; | ||
if (txn.req && txn.req.state) { parsed.query['state'] = txn.req.state; } | ||
parsed.query.error = 'access_denied'; | ||
if (txn.req && txn.req.state) { parsed.query.state = txn.req.state; } | ||
@@ -138,8 +138,8 @@ var location = url.format(parsed); | ||
if (err) { return next(err); } | ||
if (!code) { return next(new AuthorizationError('authorization server denied request', 'access_denied')); } | ||
if (!code) { return next(new AuthorizationError('Request denied by authorization server', 'access_denied')); } | ||
var parsed = url.parse(txn.redirectURI, true); | ||
delete parsed.search; | ||
parsed.query['code'] = code; | ||
if (txn.req && txn.req.state) { parsed.query['state'] = txn.req.state; } | ||
parsed.query.code = code; | ||
if (txn.req && txn.req.state) { parsed.query.state = txn.req.state; } | ||
@@ -158,7 +158,11 @@ var location = url.format(parsed); | ||
var arity = issue.length; | ||
if (arity == 5) { | ||
issue(txn.client, txn.req.redirectURI, txn.user, txn.res, issued); | ||
} else { // arity == 4 | ||
issue(txn.client, txn.req.redirectURI, txn.user, issued); | ||
try { | ||
var arity = issue.length; | ||
if (arity == 5) { | ||
issue(txn.client, txn.req.redirectURI, txn.user, txn.res, issued); | ||
} else { // arity == 4 | ||
issue(txn.client, txn.req.redirectURI, txn.user, issued); | ||
} | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
@@ -176,2 +180,2 @@ } | ||
return mod; | ||
} | ||
}; |
@@ -61,7 +61,7 @@ /** | ||
issue = options; | ||
options = null; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
if (!issue) throw new Error('OAuth 2.0 token grant middleware requires an issue function.'); | ||
if (!issue) { throw new TypeError('oauth2orize.token grant requires an issue callback'); } | ||
@@ -85,8 +85,8 @@ // For maximum flexibility, multiple scope spearators can optionally be | ||
function request(req) { | ||
var clientID = req.query['client_id'] | ||
, redirectURI = req.query['redirect_uri'] | ||
, scope = req.query['scope'] | ||
, state = req.query['state']; | ||
var clientID = req.query.client_id | ||
, redirectURI = req.query.redirect_uri | ||
, scope = req.query.scope | ||
, state = req.query.state; | ||
if (!clientID) { throw new AuthorizationError('missing client_id parameter', 'invalid_request'); } | ||
if (!clientID) { throw new AuthorizationError('Missing required parameter: client_id', 'invalid_request'); } | ||
@@ -112,3 +112,3 @@ if (scope) { | ||
state: state | ||
} | ||
}; | ||
} | ||
@@ -124,7 +124,7 @@ | ||
function response(txn, res, next) { | ||
if (!txn.redirectURI) { return next(new Error('No redirect URI available to send OAuth 2.0 response.')); } | ||
if (!txn.redirectURI) { return next(new Error('Unable to issue redirect for OAuth 2.0 transaction')); } | ||
if (!txn.res.allow) { | ||
var err = {}; | ||
err['error'] = 'access_denied'; | ||
if (txn.req && txn.req.state) { err['state'] = txn.req.state; } | ||
err.error = 'access_denied'; | ||
if (txn.req && txn.req.state) { err.state = txn.req.state; } | ||
@@ -140,9 +140,9 @@ var parsed = url.parse(txn.redirectURI); | ||
if (err) { return next(err); } | ||
if (!accessToken) { return next(new AuthorizationError('authorization server denied request', 'access_denied')); } | ||
if (!accessToken) { return next(new AuthorizationError('Request denied by authorization server', 'access_denied')); } | ||
var tok = {}; | ||
tok['access_token'] = accessToken; | ||
tok.access_token = accessToken; | ||
if (params) { utils.merge(tok, params); } | ||
tok['token_type'] = tok['token_type'] || 'bearer'; | ||
if (txn.req && txn.req.state) { tok['state'] = txn.req.state; } | ||
tok.token_type = tok.token_type || 'Bearer'; | ||
if (txn.req && txn.req.state) { tok.state = txn.req.state; } | ||
@@ -163,7 +163,11 @@ var parsed = url.parse(txn.redirectURI); | ||
var arity = issue.length; | ||
if (arity == 4) { | ||
issue(txn.client, txn.user, txn.res, issued); | ||
} else { // arity == 3 | ||
issue(txn.client, txn.user, issued); | ||
try { | ||
var arity = issue.length; | ||
if (arity == 4) { | ||
issue(txn.client, txn.user, txn.res, issued); | ||
} else { // arity == 3 | ||
issue(txn.client, txn.user, issued); | ||
} | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
@@ -181,2 +185,2 @@ } | ||
return mod; | ||
} | ||
}; |
@@ -9,5 +9,2 @@ /** | ||
// expose createServer() as the module | ||
exports = module.exports = createServer; | ||
/** | ||
@@ -24,4 +21,7 @@ * Create an OAuth 2.0 server. | ||
// expose createServer() as the module | ||
exports = module.exports = createServer; | ||
/** | ||
* Expose `.createServer()` as module method. | ||
* Export `.createServer()`. | ||
*/ | ||
@@ -32,4 +32,9 @@ exports.createServer = createServer; | ||
/** | ||
* Auto-load bundled grant middleware. | ||
* Export middleware. | ||
*/ | ||
exports.errorHandler = require('./middleware/errorHandler'); | ||
/** | ||
* Auto-load bundled grants. | ||
*/ | ||
exports.grant = {}; | ||
@@ -45,2 +50,3 @@ | ||
// alias grants | ||
exports.grant.authorizationCode = exports.grant.code; | ||
@@ -50,3 +56,3 @@ exports.grant.implicit = exports.grant.token; | ||
/** | ||
* Auto-load bundled exchange middleware. | ||
* Auto-load bundled exchanges. | ||
*/ | ||
@@ -63,2 +69,9 @@ exports.exchange = {}; | ||
// alias exchanges | ||
exports.exchange.code = exports.exchange.authorizationCode; | ||
/** | ||
* Export errors. | ||
*/ | ||
exports.AuthorizationError = require('./errors/authorizationerror'); | ||
exports.TokenError = require('./errors/tokenerror'); |
@@ -5,4 +5,3 @@ /** | ||
var utils = require('../utils') | ||
, AuthorizationError = require('../errors/authorizationerror') | ||
, BadRequestError = require('../errors/badrequesterror'); | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
@@ -100,24 +99,28 @@ | ||
*/ | ||
module.exports = function authorization(server, options, validate) { | ||
module.exports = function(server, options, validate, immediate) { | ||
if (typeof options == 'function') { | ||
immediate = validate; | ||
validate = options; | ||
options = {}; | ||
options = undefined; | ||
} | ||
options = options || {}; | ||
immediate = immediate || function (client, user, done) { return done(null, false); }; | ||
if (!server) throw new Error('OAuth 2.0 authorization middleware requires a server instance.'); | ||
if (!validate) throw new Error('OAuth 2.0 authorization middleware requires a validate function.'); | ||
if (!server) { throw new TypeError('oauth2orize.authorization middleware requires a server argument'); } | ||
if (!validate) { throw new TypeError('oauth2orize.authorization middleware requires a validate function'); } | ||
var lenTxnID = options.idLength || 8; | ||
var key = options.sessionKey || 'authorize'; | ||
var lenTxnID = options.idLength || 8 | ||
, userProperty = options.userProperty || 'user' | ||
, key = options.sessionKey || 'authorize'; | ||
return function authorization(req, res, next) { | ||
if (!req.session) { return next(new Error('OAuth 2.0 server requires session support.')); } | ||
if (!req.session) { return next(new Error('OAuth2orize requires session support. Did you forget app.use(express.session(...))?')); } | ||
var body = req.body || {} | ||
, type = req.query['response_type'] || body['response_type']; | ||
, type = req.query.response_type || body.response_type; | ||
server._parse(type, req, function(err, areq) { | ||
if (err) { return next(err); } | ||
if (!areq || !Object.keys(areq).length || (Object.keys(areq).length == 1 && areq.type)) { return next(new AuthorizationError('invalid response type', 'unsupported_response_type')); } | ||
if (!areq || !Object.keys(areq).length) { return next(new AuthorizationError('Missing required parameter: response_type', 'invalid_request')); } | ||
if (Object.keys(areq).length == 1 && areq.type) { return next(new AuthorizationError('Unsupported response type: ' + type, 'unsupported_response_type')); } | ||
@@ -129,52 +132,70 @@ function validated(err, client, redirectURI) { | ||
req.oauth2 = {}; | ||
req.oauth2.client = client; | ||
req.oauth2.redirectURI = redirectURI; | ||
if (client) { req.oauth2.client = client; } | ||
if (redirectURI) { req.oauth2.redirectURI = redirectURI; } | ||
if (err) { return next(err); } | ||
if (!client) { return next(new AuthorizationError('not authorized', 'unauthorized_client')); } | ||
if (!client) { return next(new AuthorizationError('Unauthorized client', 'unauthorized_client')); } | ||
req.oauth2.req = areq; | ||
// TODO: Add an optional `immediate` callback, which can consult a | ||
// pre-approved decision and respond immediately, along with the | ||
// ability to fall back into "transaction" mode. Currently, this | ||
// functionality can be achieved using later route middleware | ||
// after `next()`ing, but this would optimize away the need to | ||
// serialize the client into the session. | ||
req.oauth2.user = req[userProperty]; | ||
// A dialog needs to be conducted to obtain the user's approval. | ||
// Serialize a transaction to the session. The transaction will be | ||
// restored (and removed) from the session when the user allows or | ||
// denies the request. | ||
server.serializeClient(client, function(err, obj) { | ||
function immediated(err, allow, ares) { | ||
if (err) { return next(err); } | ||
var tid = utils.uid(lenTxnID); | ||
req.oauth2.transactionID = tid; | ||
var txn = {}; | ||
txn.protocol = 'oauth2'; | ||
txn.client = obj; | ||
txn.redirectURI = redirectURI; | ||
txn.req = areq; | ||
// store transaction in session | ||
var txns = req.session[key] = req.session[key] || {}; | ||
txns[tid] = txn; | ||
if (allow) { | ||
req.oauth2.res = ares || {}; | ||
req.oauth2.res.allow = true; | ||
next(); | ||
}); | ||
server._respond(req.oauth2, res, function(err) { | ||
if (err) { return next(err); } | ||
return next(new AuthorizationError('Unsupported response type: ' + req.oauth2.req.type, 'unsupported_response_type')); | ||
}); | ||
} else { | ||
// A dialog needs to be conducted to obtain the user's approval. | ||
// Serialize a transaction to the session. The transaction will be | ||
// restored (and removed) from the session when the user allows or | ||
// denies the request. | ||
server.serializeClient(client, function(err, obj) { | ||
if (err) { return next(err); } | ||
var tid = utils.uid(lenTxnID); | ||
req.oauth2.transactionID = tid; | ||
var txn = {}; | ||
txn.protocol = 'oauth2'; | ||
txn.client = obj; | ||
txn.redirectURI = redirectURI; | ||
txn.req = areq; | ||
// store transaction in session | ||
var txns = req.session[key] = req.session[key] || {}; | ||
txns[tid] = txn; | ||
next(); | ||
}); | ||
} | ||
} | ||
var arity = immediate.length; | ||
if (arity == 4) { | ||
immediate(req.oauth2.client, req.oauth2.user, req.oauth2.req.scope, immediated); | ||
} else { // arity == 3 | ||
immediate(req.oauth2.client, req.oauth2.user, immediated); | ||
} | ||
} | ||
var arity = validate.length; | ||
if (arity == 3) { | ||
validate(areq.clientID, areq.redirectURI, validated); | ||
} else if (arity == 4) { | ||
validate(areq.clientID, areq.redirectURI, areq.scope, validated); | ||
} else if (arity == 5) { | ||
validate(areq.clientID, areq.redirectURI, areq.scope, areq.type, validated); | ||
} else { // arity == 2 | ||
validate(areq, validated); | ||
try { | ||
var arity = validate.length; | ||
if (arity == 3) { | ||
validate(areq.clientID, areq.redirectURI, validated); | ||
} else if (arity == 4) { | ||
validate(areq.clientID, areq.redirectURI, areq.scope, validated); | ||
} else if (arity == 5) { | ||
validate(areq.clientID, areq.redirectURI, areq.scope, areq.type, validated); | ||
} else { // arity == 2 | ||
validate(areq, validated); | ||
} | ||
} catch (ex) { | ||
return next(ex); | ||
} | ||
}); | ||
} | ||
} | ||
}; | ||
}; |
/** | ||
* Module dependencies. | ||
*/ | ||
var util = require('util') | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
var AuthorizationError = require('../errors/authorizationerror') | ||
, ForbiddenError = require('../errors/forbiddenerror'); | ||
/** | ||
* Handle authorization descisions from resource owners. | ||
* Handle authorization decisions from resource owners. | ||
* | ||
@@ -68,6 +68,6 @@ * Obtaining authorization via OAuth 2.0 consists of a sequence of discrete | ||
*/ | ||
module.exports = function descision(server, options, parse) { | ||
module.exports = function(server, options, parse) { | ||
if (typeof options == 'function') { | ||
parse = options; | ||
options = {}; | ||
options = undefined; | ||
} | ||
@@ -77,3 +77,3 @@ options = options || {}; | ||
if (!server) throw new Error('OAuth 2.0 descision middleware requires a server instance.'); | ||
if (!server) { throw new TypeError('oauth2orize.decision middleware requires a server argument'); } | ||
@@ -84,7 +84,7 @@ var cancelField = options.cancelField || 'cancel' | ||
return function descision(req, res, next) { | ||
if (!req.session) { return next(new Error('OAuth 2.0 server requires session support.')); } | ||
if (!req.session[key]) { return next(new Error('Invalid OAuth 2.0 session key.')); } | ||
if (!req.body) { return next(new Error('OAuth 2.0 server requires body parsing.')); } | ||
if (!req.oauth2) { return next(new Error('OAuth 2.0 transaction not found.')); } | ||
return function decision(req, res, next) { | ||
if (!req.session) { return next(new Error('OAuth2orize requires session support. Did you forget app.use(express.session(...))?')); } | ||
if (!req.body) { return next(new Error('OAuth2orize requires body parsing. Did you forget app.use(express.bodyParser())?')); } | ||
if (!req.oauth2) { return next(new Error('OAuth2orize requires transaction support. Did you forget oauth2orize.transactionLoader(...)?')); } | ||
if (!req.session[key]) { return next(new ForbiddenError('Unable to load OAuth 2.0 transactions from session')); } | ||
@@ -109,10 +109,10 @@ parse(req, function(err, ares) { | ||
res.end(chunk, encoding); | ||
} | ||
}; | ||
server._respond(req.oauth2, res, function(err) { | ||
if (err) { return next(err); } | ||
return next(new AuthorizationError('invalid response type', 'unsupported_response_type')); | ||
return next(new AuthorizationError('Unsupported response type: ' + req.oauth2.req.type, 'unsupported_response_type')); | ||
}); | ||
}); | ||
} | ||
} | ||
}; | ||
}; |
/** | ||
* Module dependencies. | ||
*/ | ||
var url = require('url'); | ||
var url = require('url') | ||
, qs = require('querystring') | ||
, UnorderedList = require('../unorderedlist'); | ||
@@ -44,10 +46,8 @@ | ||
*/ | ||
module.exports = function errorHandler(options) { | ||
module.exports = function(options) { | ||
options = options || {}; | ||
var mode = options.mode || 'direct'; | ||
var mode = options.mode || 'direct' | ||
, fragment = options.fragment || ['token']; | ||
// TODO: Error response should be added in the URL fragment, if the client is | ||
// requesting an implicit token. | ||
return function errorHandler(err, req, res, next) { | ||
@@ -63,5 +63,5 @@ if (mode == 'direct') { | ||
var e = {}; | ||
e['error'] = err.code || 'server_error'; | ||
if (err.message) { e['error_description'] = err.message; } | ||
if (err.uri) { e['error_uri'] = err.uri; } | ||
e.error = err.code || 'server_error'; | ||
if (err.message) { e.error_description = err.message; } | ||
if (err.uri) { e.error_uri = err.uri; } | ||
@@ -77,11 +77,31 @@ res.setHeader('Content-Type', 'application/json'); | ||
var redirectURI = req.oauth2.redirectURI; | ||
var parsed = url.parse(redirectURI, true); | ||
delete parsed.search; | ||
parsed.query['error'] = err.code || 'server_error'; | ||
if (err.message) { parsed.query['error_description'] = err.message; } | ||
if (err.uri) { parsed.query['error_uri'] = err.uri; } | ||
if (req.oauth2.req && req.oauth2.req.state) { parsed.query['state'] = req.oauth2.req.state; } | ||
var enc = 'query'; | ||
if (req.oauth2 && req.oauth2.req) { | ||
var type = new UnorderedList(req.oauth2.req.type); | ||
// In accordance with [OAuth 2.0 Multiple Response Type Encoding | ||
// Practices - draft 08](http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html), | ||
// if the response type contains any value that requires fragment | ||
// encoding, the response will be fragment encoded. | ||
if (type.containsAny(fragment)) { enc = 'fragment'; } | ||
} | ||
var location = url.format(parsed); | ||
var redirectURI = req.oauth2.redirectURI | ||
, uri = url.parse(redirectURI, true); | ||
if (enc == 'fragment') { | ||
var hash = {}; | ||
hash.error = err.code || 'server_error'; | ||
if (err.message) { hash.error_description = err.message; } | ||
if (err.uri) { hash.error_uri = err.uri; } | ||
if (req.oauth2.req && req.oauth2.req.state) { hash.state = req.oauth2.req.state; } | ||
uri.hash = qs.stringify(hash); | ||
} else { | ||
delete uri.search; | ||
uri.query.error = err.code || 'server_error'; | ||
if (err.message) { uri.query.error_description = err.message; } | ||
if (err.uri) { uri.query.error_uri = err.uri; } | ||
if (req.oauth2.req && req.oauth2.req.state) { uri.query.state = req.oauth2.req.state; } | ||
} | ||
var location = url.format(uri); | ||
res.redirect(location); | ||
@@ -91,3 +111,3 @@ } else { | ||
} | ||
} | ||
} | ||
}; | ||
}; |
/** | ||
* Module dependencies. | ||
*/ | ||
var AuthorizationError = require('../errors/authorizationerror'); | ||
var TokenError = require('../errors/tokenerror'); | ||
@@ -51,12 +51,12 @@ | ||
if (!server) throw new Error('OAuth 2.0 token middleware requires a server instance.'); | ||
if (!server) { throw new TypeError('oauth2orize.token middleware requires a server argument'); } | ||
return function token(req, res, next) { | ||
var type = req.body['grant_type']; | ||
var type = req.body.grant_type; | ||
server._exchange(type, req, res, function(err) { | ||
if (err) { return next(err); } | ||
return next(new AuthorizationError('invalid grant type', 'unsupported_grant_type')); | ||
return next(new TokenError('Unsupported grant type: ' + type, 'unsupported_grant_type')); | ||
}); | ||
} | ||
} | ||
}; | ||
}; |
/** | ||
* Module dependencies. | ||
*/ | ||
var util = require('util') | ||
, AuthorizationError = require('../errors/authorizationerror'); | ||
var AuthorizationError = require('../errors/authorizationerror') | ||
, BadRequestError = require('../errors/badrequesterror') | ||
, ForbiddenError = require('../errors/forbiddenerror'); | ||
@@ -26,6 +27,6 @@ | ||
*/ | ||
module.exports = function transactionLoader(server, options) { | ||
module.exports = function(server, options) { | ||
options = options || {}; | ||
if (!server) throw new Error('OAuth 2.0 transactionLoader middleware requires a server instance.'); | ||
if (!server) { throw new TypeError('oauth2orize.transactionLoader middleware requires a server argument'); } | ||
@@ -36,4 +37,4 @@ var field = options.transactionField || 'transaction_id' | ||
return function transactionLoader(req, res, next) { | ||
if (!req.session) { return next(new Error('OAuth 2.0 server requires session support.')); } | ||
if (!req.session[key]) { return next(new Error('Invalid OAuth 2.0 session key.')); } | ||
if (!req.session) { return next(new Error('OAuth2orize requires session support. Did you forget app.use(express.session(...))?')); } | ||
if (!req.session[key]) { return next(new ForbiddenError('Unable to load OAuth 2.0 transactions from session')); } | ||
@@ -44,5 +45,5 @@ var query = req.query || {} | ||
if (!tid) { return next(); } | ||
if (!tid) { return next(new BadRequestError('Missing required parameter: ' + field)); } | ||
var txn = req.session[key][tid]; | ||
if (!txn) { return next(); } | ||
if (!txn) { return next(new ForbiddenError('Unable to load OAuth 2.0 transaction: ' + tid)); } | ||
@@ -56,4 +57,4 @@ server.deserializeClient(txn.client, function(err, client) { | ||
delete req.session[key][tid]; | ||
return next(new AuthorizationError('no longer authorized', 'unauthorized_client')); | ||
}; | ||
return next(new AuthorizationError('Unauthorized client', 'unauthorized_client')); | ||
} | ||
@@ -67,3 +68,3 @@ req.oauth2 = {}; | ||
}); | ||
} | ||
} | ||
}; | ||
}; |
/** | ||
* Module dependencies. | ||
*/ | ||
var utils = require('./utils') | ||
, UnorderedList = require('./unorderedlist') | ||
var UnorderedList = require('./unorderedlist') | ||
, authorization = require('./middleware/authorization') | ||
@@ -11,2 +10,3 @@ , decision = require('./middleware/decision') | ||
, errorHandler = require('./middleware/errorHandler') | ||
, utils = require('./utils') | ||
, debug = require('debug')('oauth2orize'); | ||
@@ -23,7 +23,7 @@ | ||
this._resHandlers = []; | ||
this._exchangers = []; | ||
this._exchanges = []; | ||
this._serializers = []; | ||
this._deserializers = []; | ||
}; | ||
} | ||
@@ -81,10 +81,10 @@ /** | ||
if (phase == 'request') { | ||
debug('register parser %s %s', type || '*', fn.name || 'anonymous'); | ||
debug('register request parser %s %s', type || '*', fn.name || 'anonymous'); | ||
this._reqParsers.push({ type: type, handle: fn }); | ||
} else if (phase == 'response') { | ||
debug('register responder %s %s', type || '*', fn.name || 'anonymous'); | ||
debug('register response handler %s %s', type || '*', fn.name || 'anonymous'); | ||
this._resHandlers.push({ type: type, handle: fn }); | ||
} | ||
return this; | ||
} | ||
}; | ||
@@ -118,5 +118,5 @@ /** | ||
debug('register exchanger %s %s', type || '*', fn.name || 'anonymous'); | ||
this._exchangers.push({ type: type, handle: fn }); | ||
this._exchanges.push({ type: type, handle: fn }); | ||
return this; | ||
} | ||
}; | ||
@@ -131,3 +131,3 @@ /** | ||
return authorization(this, options, validate); | ||
} | ||
}; | ||
@@ -140,7 +140,7 @@ /** | ||
Server.prototype.decision = function(options, parse) { | ||
if (options && options.loadTransaction == false) { | ||
if (options && options.loadTransaction === false) { | ||
return decision(this, options, parse); | ||
} | ||
return [transactionLoader(this, options), decision(this, options, parse)]; | ||
} | ||
}; | ||
@@ -154,3 +154,3 @@ /** | ||
return token(this, options); | ||
} | ||
}; | ||
@@ -164,3 +164,3 @@ /** | ||
return errorHandler(options); | ||
} | ||
}; | ||
@@ -196,12 +196,12 @@ /** | ||
if (!layer) { | ||
return done(new Error('Failed to serialize client. Register serialization function using serializeClient().')); | ||
return done(new Error('Failed to serialize client. Register serialization function using serializeClient().')); | ||
} | ||
try { | ||
layer(client, function(e, o) { pass(i + 1, e, o); } ) | ||
} catch(e) { | ||
return done(e); | ||
layer(client, function(e, o) { pass(i + 1, e, o); } ); | ||
} catch (ex) { | ||
return done(ex); | ||
} | ||
})(0); | ||
} | ||
}; | ||
@@ -242,12 +242,12 @@ /** | ||
if (!layer) { | ||
return done(new Error('Failed to deserialize client. Register deserialization function using deserializeClient().')); | ||
return done(new Error('Failed to deserialize client. Register deserialization function using deserializeClient().')); | ||
} | ||
try { | ||
layer(obj, function(e, c) { pass(i + 1, e, c); } ) | ||
} catch(e) { | ||
return done(e); | ||
layer(obj, function(e, c) { pass(i + 1, e, c); } ); | ||
} catch (ex) { | ||
return done(ex); | ||
} | ||
})(0); | ||
} | ||
}; | ||
@@ -292,7 +292,7 @@ | ||
} | ||
} catch(e) { | ||
return cb(e); | ||
} catch (ex) { | ||
return cb(ex); | ||
} | ||
})(0); | ||
} | ||
}; | ||
@@ -325,8 +325,8 @@ /** | ||
} | ||
} catch (e) { | ||
return cb(e); | ||
} catch (ex) { | ||
return cb(ex); | ||
} | ||
} | ||
next(); | ||
} | ||
}; | ||
@@ -343,3 +343,3 @@ /** | ||
Server.prototype._exchange = function(type, req, res, cb) { | ||
var stack = this._exchangers | ||
var stack = this._exchanges | ||
, idx = 0; | ||
@@ -360,8 +360,8 @@ | ||
} | ||
} catch (e) { | ||
return cb(e); | ||
} catch (ex) { | ||
return cb(ex); | ||
} | ||
} | ||
next(); | ||
} | ||
}; | ||
@@ -368,0 +368,0 @@ |
@@ -22,3 +22,3 @@ /** | ||
UnorderedList.prototype.equalTo = function(other) { | ||
if (!other instanceof UnorderedList) { | ||
if (!(other instanceof UnorderedList)) { | ||
other = new UnorderedList(other); | ||
@@ -35,5 +35,30 @@ } | ||
return true; | ||
} | ||
}; | ||
/** | ||
* Check if list contains `val` | ||
* | ||
* @param {String} val | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
UnorderedList.prototype.contains = function(val) { | ||
return this._items.indexOf(val) != -1; | ||
}; | ||
/** | ||
* Check if list contains any element in `arr` | ||
* | ||
* @param {Array} arr | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
UnorderedList.prototype.containsAny = function(arr) { | ||
for (var i = 0, len = arr.length; i < len; i++) { | ||
if (this._items.indexOf(arr[i]) != -1) { return true; } | ||
} | ||
return false; | ||
}; | ||
/** | ||
* String representation of list. | ||
@@ -46,3 +71,3 @@ * | ||
return this._items.join(' '); | ||
} | ||
}; | ||
@@ -57,7 +82,7 @@ /** | ||
return this._items.length; | ||
} | ||
}; | ||
/** | ||
* Expose `UnorderedList`. | ||
*/ | ||
*/ | ||
module.exports = UnorderedList; |
@@ -1,62 +0,2 @@ | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var crypto = require('crypto'); | ||
/** | ||
* Merge object b with object a. | ||
* | ||
* var a = { foo: 'bar' } | ||
* , b = { bar: 'baz' }; | ||
* | ||
* utils.merge(a, b); | ||
* // => { foo: 'bar', bar: 'baz' } | ||
* | ||
* @param {Object} a | ||
* @param {Object} b | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
exports.merge = function(a, b){ | ||
if (a && b) { | ||
for (var key in b) { | ||
a[key] = b[key]; | ||
} | ||
} | ||
return a; | ||
}; | ||
/** | ||
* Return a unique identifier with the given `len`. | ||
* | ||
* utils.uid(10); | ||
* // => "FDaS435D2z" | ||
* | ||
* @param {Number} len | ||
* @return {String} | ||
* @api private | ||
*/ | ||
exports.uid = function(len) { | ||
var buf = [] | ||
, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' | ||
, charlen = chars.length; | ||
for (var i = 0; i < len; ++i) { | ||
buf.push(chars[getRandomInt(0, charlen - 1)]); | ||
} | ||
return buf.join(''); | ||
}; | ||
/** | ||
* Retrun a random int, used by `utils.uid()` | ||
* | ||
* @param {Number} min | ||
* @param {Number} max | ||
* @return {Number} | ||
* @api private | ||
*/ | ||
function getRandomInt(min, max) { | ||
return Math.floor(Math.random() * (max - min + 1)) + min; | ||
} | ||
exports.merge = require('utils-merge'); | ||
exports.uid = require('uid2'); |
{ | ||
"name": "oauth2orize", | ||
"version": "0.1.0", | ||
"version": "1.0.0", | ||
"description": "OAuth 2.0 authorization server toolkit for Node.js.", | ||
"keywords": ["oauth", "oauth2", "connect", "express", "middleware", "http", "api"], | ||
"keywords": [ | ||
"oauth", | ||
"oauth2", | ||
"auth", | ||
"authz", | ||
"authorization", | ||
"connect", | ||
"express", | ||
"passport", | ||
"middleware" | ||
], | ||
"repository": { | ||
@@ -13,18 +23,31 @@ "type": "git", | ||
}, | ||
"author": { "name": "Jared Hanson", "email": "jaredhanson@gmail.com", "url": "http://www.jaredhanson.net/" }, | ||
"licenses": [ { | ||
"type": "MIT", | ||
"url": "http://www.opensource.org/licenses/MIT" | ||
} ], | ||
"author": { | ||
"name": "Jared Hanson", | ||
"email": "jaredhanson@gmail.com", | ||
"url": "http://www.jaredhanson.net/" | ||
}, | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "http://www.opensource.org/licenses/MIT" | ||
} | ||
], | ||
"main": "./lib", | ||
"dependencies": { | ||
"uid2": "0.0.x", | ||
"utils-merge": "1.x.x", | ||
"debug": "0.7.x" | ||
}, | ||
"devDependencies": { | ||
"vows": "0.6.x" | ||
"mocha": "1.x.x", | ||
"chai": "1.x.x", | ||
"chai-connect-middleware": "0.1.x", | ||
"chai-oauth2orize-grant": "0.1.x" | ||
}, | ||
"engines": { | ||
"node": ">= 0.4.0" | ||
}, | ||
"scripts": { | ||
"test": "NODE_PATH=lib node_modules/.bin/vows test/*-test.js test/**/*-test.js" | ||
}, | ||
"engines": { "node": ">= 0.4.0" } | ||
"test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
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
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
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
76579
24
1827
1
0
186
3
4
+ Addeduid2@0.0.x
+ Addedutils-merge@1.x.x
+ Addeduid2@0.0.4(transitive)
+ Addedutils-merge@1.0.1(transitive)