oauth2orize
Advanced tools
Comparing version
@@ -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
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
18.63%24
20%1827
6.78%1
-50%0
-100%186
Infinity%3
200%4
300%+ Added
+ Added
+ Added
+ Added