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

loopback-component-oauth2

Package Overview
Dependencies
Maintainers
4
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

loopback-component-oauth2 - npm Package Compare versions

Comparing version 2.0.0-beta4 to 2.0.0-beta5

common/models/oauth-client-application.json

16

CHANGES.md

@@ -0,4 +1,20 @@

2015-03-10, Version 2.0.0-beta5
===============================
* Add tokenTypes (Raymond Feng)
* Return anonymous user (Raymond Feng)
* Tidy up application validation (Raymond Feng)
* Add permission management and subject support for client credentials (Raymond Feng)
* Pull in changes from upstream (Raymond Feng)
2015-01-30, Version 2.0.0-beta4
===============================
* v2.0.0-beta4 (Raymond Feng)
* Make sure bodyParser doesn't interfere with proxy (Raymond Feng)

@@ -5,0 +21,0 @@

8

common/models/oauth-scope.json

@@ -6,11 +6,9 @@ {

"type": "string",
"index": {
"unique": true
}
"id": true,
"generated": false
},
"description": "string",
"iconURL": "string",
"ttl": "number",
"requiredAccessLevel": "number"
"ttl": "number"
}
}
var util = require('util');
/**
* Module dependencies.
*/
var OAuth2Error = require('./oauth2error');
/**
* `AuthorizationError` error.

@@ -20,15 +25,11 @@ *

Error.call(this);
OAuth2Error.call(this, message, code, uri, status);
Error.captureStackTrace(this, arguments.callee);
this.name = 'AuthorizationError';
this.message = message;
this.code = code || 'server_error';
this.uri = uri;
this.status = status || 500;
}
/**
* Inherit from `Error`.
* Inherit from `OAuth2Error`.
*/
util.inherits(AuthorizationError, Error);
util.inherits(AuthorizationError, OAuth2Error);

@@ -35,0 +36,0 @@ /**

var util = require('util');
/**
* Module dependencies.
*/
var OAuth2Error = require('./oauth2error');
/**
* `TokenError` error.

@@ -19,15 +24,11 @@ *

Error.call(this);
OAuth2Error.call(this, message, code, uri, status);
Error.captureStackTrace(this, arguments.callee);
this.name = 'TokenError';
this.message = message;
this.code = code || 'server_error';
this.uri = uri;
this.status = status || 500;
}
/**
* Inherit from `Error`.
* Inherit from `OAuth2Error`.
*/
util.inherits(TokenError, Error);
util.inherits(TokenError, OAuth2Error);

@@ -34,0 +35,0 @@ /**

@@ -101,4 +101,6 @@ /**

if (err) { return next(err); }
if (!accessToken) { return next(new TokenError('Invalid client credentials', 'invalid_grant')); }
if (refreshToken && typeof refreshToken == 'object') {
if (!accessToken) {
return next(new TokenError('Invalid client credentials', 'invalid_grant'));
}
if (refreshToken && typeof refreshToken === 'object') {
params = refreshToken;

@@ -123,3 +125,7 @@ refreshToken = null;

var arity = issue.length;
if (arity === 3) {
if (arity === 4) {
// Allow subject (username or email) to be specified
var subject = req.body.sub || req.body.subject || req.body.username;
issue(client, subject, scope, issued);
} else if (arity === 3) {
issue(client, scope, issued);

@@ -126,0 +132,0 @@ } else { // arity == 2

@@ -5,2 +5,3 @@ /**

var utils = require('../utils')
, helpers = require('../oauth2-helper')
, AuthorizationError = require('../errors/authorizationerror');

@@ -100,3 +101,3 @@

module.exports = function(server, options, validate, immediate) {
if (typeof options == 'function') {
if (typeof options === 'function') {
immediate = validate;

@@ -155,33 +156,14 @@ validate = options;

var validURIs = client.callbackUrls || client.redirectUris || [];
if (validURIs) {
if (typeof validURIs === 'string') {
validURIs = [validURIs];
}
}
var valid = true;
validURIs = helpers.normalizeList(validURIs);
if (!redirectURI) {
redirectURI = validURIs[0];
} else {
for (var i = 0, n = validURIs.length; i < n; i++) {
valid = false;
if (redirectURI.indexOf(validURIs[i]) === 0) {
valid = true;
break;
}
}
}
if (!valid) {
// The redirect_uri doesn't match pre-registered ones
return next(new AuthorizationError(
'Invalid request: redirect_uri "' +
redirectURI +
'" is invalid',
'invalid_request'));
}
if (!redirectURI) {
// The redirect uri is missing
return next(new AuthorizationError(
'Invalid request: redirect_uri is missing',
'Invalid request: redirect_uri is missing',
'invalid_request'));
}
helpers.validateClient(client, {redirectURI: redirectURI}, next);
req.oauth2.redirectURI = redirectURI;

@@ -192,6 +174,6 @@

function immediated(err, allow, ares) {
function immediated(err, allow, info, locals) {
if (err) { return next(err); }
if (allow) {
req.oauth2.res = ares || {};
req.oauth2.res = info || {};
req.oauth2.res.allow = true;

@@ -213,2 +195,18 @@

req.oauth2.transactionID = tid;
// Add info and locals to `req.oauth2`, where they will be
// available to the next middleware. Since this is a
// non-immediate response, the next middleware's responsibility is
// to prompt the user to allow or deny access. `info` and
// `locals` are passed along as they may be of assistance when
// rendering the prompt.
//
// Note that `info` is also serialized into the transaction, where
// it can further be utilized in the `decision` middleware after
// the user submits the prompt's form. As such, `info` should be
// a normal JSON object, so that it can be correctly serialized
// into the session. `locals` is only carried through to the
// middleware chain for the current request, so it may contain
// instantiated classes that don't serialize cleanly.
req.oauth2.info = info;
req.oauth2.locals = locals;

@@ -220,2 +218,3 @@ var txn = {};

txn.req = areq;
txn.info = info;
// store transaction in session

@@ -222,0 +221,0 @@ var txns = req.session[key] = req.session[key] || {};

@@ -91,3 +91,3 @@ /**

var enc = 'query';
if (req.oauth2 && req.oauth2.req) {
if (req.oauth2.req) {
var type = new UnorderedList(req.oauth2.req.type);

@@ -94,0 +94,0 @@ // In accordance with [OAuth 2.0 Multiple Response Type Encoding

@@ -62,2 +62,3 @@ /**

req.oauth2.req = txn.req;
req.oauth2.info = txn.info;
next();

@@ -64,0 +65,0 @@ });

var debug = require('debug')('loopback:oauth2:models');
var helpers = require('../oauth2-helper');
/**

@@ -10,5 +11,2 @@ * Create oAuth 2.0 metadata models

options = options || {};
var userModel = options.userModel || loopback.getModelByType(loopback.User);
var applicationModel = options.applicationModel
|| loopback.getModelByType(loopback.Application);

@@ -21,4 +19,10 @@ var dataSource = options.dataSource;

var oauth2 = require('./oauth2-models')(dataSource);
var userModel = options.userModel || loopback.getModelByType(loopback.User);
var applicationModel = options.applicationModel
|| loopback.getModelByType(loopback.Application);
var oAuthTokenModel = oauth2.OAuthToken;
var oAuthAuthorizationCodeModel = oauth2.OAuthAuthorizationCode;
var oAuthPermissionModel = oauth2.OAuthPermission;

@@ -37,2 +41,8 @@ oAuthTokenModel.belongsTo(userModel,

oAuthPermissionModel.belongsTo(userModel,
{as: 'user', foreignKey: 'userId'});
oAuthPermissionModel.belongsTo(applicationModel,
{as: 'application', foreignKey: 'appId'});
var getTTL = typeof options.getTTL === 'function' ? options.getTTL :

@@ -110,12 +120,24 @@ function(grantType, clientId, resourceOwner, scopes) {

token.save = function(token, clientId, resourceOwner, scopes, refreshToken, done) {
var tokenObj;
if (arguments.length === 2 && typeof token === 'object') {
// save(token, cb)
tokenObj = token;
done = clientId;
}
var ttl = getTTL('token', clientId, resourceOwner, scopes);
oAuthTokenModel.create({
id: token,
appId: clientId,
userId: resourceOwner,
scopes: scopes,
issuedAt: new Date(),
expiresIn: ttl,
refreshToken: refreshToken
}, done);
if (!tokenObj) {
tokenObj = {
id: token,
appId: clientId,
userId: resourceOwner,
scopes: scopes,
issuedAt: new Date(),
expiresIn: ttl,
refreshToken: refreshToken
};
}
tokenObj.expiresIn = ttl;
tokenObj.issuedAt = new Date();
tokenObj.expiredAt = new Date(tokenObj.issuedAt.getTime() + ttl * 1000);
oAuthTokenModel.create(tokenObj, done);
};

@@ -131,12 +153,73 @@

code.save = function(code, clientId, redirectURI, resourceOwner, scopes, done) {
var codeObj;
if (arguments.length === 2 && typeof token === 'object') {
// save(code, cb)
codeObj = code;
done = clientId;
}
var ttl = getTTL('code', clientId, resourceOwner, scopes);
oAuthAuthorizationCodeModel.create({
id: code,
if (!codeObj) {
codeObj = {
id: code,
appId: clientId,
userId: resourceOwner,
scopes: scopes,
redirectURI: redirectURI
};
}
codeObj.expiresIn = ttl;
codeObj.issuedAt = new Date();
codeObj.expiredAt = new Date(codeObj.issuedAt.getTime() + ttl * 1000);
oAuthAuthorizationCodeModel.create(codeObj, done);
};
var oAuthPermissionModel = oauth2.OAuthPermission;
var permission = {};
permission.find = function(appId, userId, done) {
oAuthPermissionModel.findOne({where: {
appId: appId,
userId: userId
}}, done);
};
/*
* Check if a client app is authorized by the user
*/
permission.isAuthorized = function(appId, userId, scopes, done) {
permission.find(appId, userId, function(err, perm) {
if (err) {
return done(err);
}
if (!perm) {
return done(null, false);
}
var ok = helpers.isScopeAuthorized(scopes, perm.scopes);
var info = ok ? { authorized: true} : {};
return done(null, ok, info);
});
};
/*
* Grant permissions to a client app by a user
*/
permission.addPermission = function(appId, userId, scopes, done) {
oAuthPermissionModel.findOrCreate({where: {
appId: appId,
userId: userId
}}, {
appId: appId,
userId: userId,
scopes: scopes,
redirectURI: redirectURI,
userId: resourceOwner,
appId: clientId,
issuedAt: new Date(),
expiresIn: ttl
}, done);
issuedAt: new Date()
}, function(err, perm, created) {
if (created) {
return done(err, perm, created);
} else {
if (helpers.isScopeAuthorized(scopes, perm.scopes)) {
return done(err, perm);
} else {
perm.updateAttributes({scopes: helpers.normalizeList(scopes)}, done);
}
}
});
};

@@ -149,3 +232,4 @@

accessTokens: token,
authorizationCodes: code
authorizationCodes: code,
permissions: permission
};

@@ -152,0 +236,0 @@

var tokenDef = require('../../common/models/oauth-token.json');
var authorizationCodeDef =
require('../../common/models/oauth-authorization-code.json');
var clientRegistrationDef =
require('../../common/models/oauth-client-registration.json');
var clientApplicationDef =
require('../../common/models/oauth-client-application.json');
var permissionDef =

@@ -14,2 +14,14 @@ require('../../common/models/oauth-permission.json');

// Remove proerties that will confuse LB
function getSettings(def) {
var settings = {};
for (var s in def) {
if (s === 'name' || s === 'properties') {
continue;
} else {
settings[s] = def[s];
}
}
}
module.exports = function(dataSource) {

@@ -19,23 +31,33 @@

var OAuthToken = dataSource.createModel(
tokenDef.name, tokenDef.properties);
tokenDef.name, tokenDef.properties, getSettings(tokenDef));
// "OAuth authorization code"
var OAuthAuthorizationCode = dataSource.createModel(
authorizationCodeDef.name, authorizationCodeDef.properties);
authorizationCodeDef.name,
authorizationCodeDef.properties,
getSettings(authorizationCodeDef));
// "OAuth client registration record"
var ClientRegistration = dataSource.createModel(
clientRegistrationDef.name, clientRegistrationDef.properties);
var OAuthClientApplication = dataSource.createModel(
clientApplicationDef.name,
clientApplicationDef.properties,
getSettings(clientApplicationDef));
// "OAuth permission"
var OAuthPermission = dataSource.createModel(
permissionDef.name, permissionDef.properties);
permissionDef.name,
permissionDef.properties,
getSettings(permissionDef));
// "OAuth scope"
var OAuthScope = dataSource.createModel(
scopeDef.name, scopeDef.properties);
scopeDef.name,
scopeDef.properties,
scopeDef);
// "OAuth scope mapping"
var OAuthScopeMapping = dataSource.createModel(
scopeMappingDef.name, scopeMappingDef.properties);
scopeMappingDef.name,
scopeMappingDef.properties,
getSettings(scopeMappingDef));

@@ -45,3 +67,3 @@ return {

OAuthAuthorizationCode: OAuthAuthorizationCode,
ClientRegistration: ClientRegistration,
OAuthClientApplication: OAuthClientApplication,
OAuthPermission: OAuthPermission,

@@ -48,0 +70,0 @@ OAuthScope: OAuthScope,

@@ -5,7 +5,7 @@ /**

var url = require('url')
, async = require('async')
, oauth2Provider = require('./oauth2orize')
, scopeValidator = require('./scope')
, TokenError = require('./errors/tokenerror')
, AuthorizationError = require('./errors/authorizationerror')
, utils = require('./utils')
, helpers = require('./oauth2-helper')
, modelBuilder = require('./models/index')

@@ -17,176 +17,12 @@ , debug = require('debug')('loopback:oauth2')

, BasicStrategy = require('passport-http').BasicStrategy
, ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy
, BearerStrategy = require('passport-http-bearer').Strategy
, ClientJWTBearerStrategy = require('./strategy/jwt-bearer').Strategy;
, ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
function clientInfo(client) {
if (!client) {
return client;
}
return client.id + ',' + client.name;
}
var clientInfo = helpers.clientInfo;
var userInfo = helpers.userInfo;
var isExpired = helpers.isExpired;
var validateClient = helpers.validateClient;
function userInfo(user) {
if (!user) {
return user;
}
return user.id + ',' + user.username + ',' + user.email;
}
var setupResourceServer = require('./resource-server');
function isExpired(tokenOrCode) {
var issuedTime =
(tokenOrCode.issuedAt && tokenOrCode.issuedAt.getTime()) || -1;
var now = Date.now();
var expirationTime =
(tokenOrCode.expiredAt && tokenOrCode.expiredAt.getTime()) || -1;
if (expirationTime === -1 && issuedTime !== -1 &&
typeof tokenOrCode.expiresIn === 'number') {
expirationTime = issuedTime + tokenOrCode.expiresIn * 1000;
}
return now > expirationTime;
}
/**
* Set up oAuth 2.0 strategies
* @param {Object} app App instance
* @param {Object} options Options
* @param {Object} models oAuth 2.0 metadata models
* @param {Boolean} jwt if jwt-bearer should be enabled
* @returns {Function}
*/
function setupResourceServer(app, options, models, jwt) {
/**
* BearerStrategy
*
* This strategy is used to authenticate users based on an access token (aka a
* bearer token). The user must have previously authorized a client
* application, which is issued an access token to make requests on behalf of
* the authorizing user.
*/
passport.use(new BearerStrategy({passReqToCallback: true},
function(req, accessToken, done) {
debug('Verifying access token %s', accessToken);
models.accessTokens.find(accessToken, function(err, token) {
if (err || !token) {
return done(err);
}
debug('Access token found: %j', token);
if (isExpired(token)) {
return done(new TokenError('Access token is expired',
'invalid_grant'));
}
var userId = token.userId || token.resourceOwner;
var appId = token.appId || token.clientId;
var user, app;
async.parallel([
function lookupUser(done) {
if (userId == null) {
return process.nextTick(done);
}
models.users.find(userId, function(err, u) {
if (err) {
return done(err);
}
if (!u) {
return done(
new TokenError('Access token has invalid user id: ' +
userId, 'invalid_grant'));
}
debug('User found: %s', userInfo(u));
user = u;
done();
});
},
function lookupApp(done) {
if (appId == null) {
return process.nextTick(done);
}
models.clients.find(appId, function(err, a) {
if (err) {
return done(err);
}
if (!a) {
return done(
new TokenError('Access token has invalid app id: ' + appId,
'invalid_grant'));
}
debug('Client found: %s', clientInfo(a));
app = a;
done();
});
}], function(err) {
if (err) {
return done(err);
}
var authInfo = { accessToken: token, user: user, app: app };
req.accessToken = token;
done(null, user, authInfo);
});
});
})
);
/**
* JWT bearer token
*/
if (jwt) {
passport.use('oauth2-jwt-bearer', new ClientJWTBearerStrategy(
{audience: options.tokenPath || '/oauth/token', passReqToCallback: true},
function(req, iss, header, done) {
debug('Looking up public key for %s', iss);
models.clients.findByClientId(iss, function(err, client) {
if (err) {
return done(err);
}
if (!client) {
return done(null, false);
}
return done(null, client.publicKey);
});
},
function(req, iss, header, done) {
models.clients.findByClientId(iss, function(err, client) {
if (err) {
return done(err);
}
if (!client) {
return done(null, false);
}
return done(null, client);
});
}
));
}
/**
* Return the middleware chain to enfore oAuth 2.0 authentication and
* authorization
* @param {Object} [options] Options object
* - scope
* - jwt
*/
function authenticate(options) {
options = options || {};
var authenticators = [];
var scopeHandler = scopeValidator(options.scope);
authenticators = [passport.authenticate('bearer', options)];
if (jwt && options.jwt) {
authenticators.push(passport.authenticate('oauth2-jwt-bearer', options));
}
if (options.scope) {
authenticators.push(scopeHandler);
}
authenticators.push(oauth2Provider.errorHandler());
return authenticators;
}
return authenticate;
}
/**
*

@@ -276,21 +112,46 @@ * @param {Object} app The app instance

*/
var codeGrant;
if (supportedGrantTypes.indexOf('authorizationCode') !== -1) {
server.grant(oauth2Provider.grant.code(
codeGrant = server.grant(oauth2Provider.grant.code(
{ allowsPost: options.allowsPostForAuthorization},
function(client, redirectURI, user, scope, ares, done) {
var code = generateToken({
grant: 'Authorization Code',
client: client,
user: user,
if (validateClient(client, {
scope: scope,
redirectURI: redirectURI
});
redirectURI: redirectURI,
grantType: 'authorization_code'
}, done)) {
return;
}
debug('Generating authorization code: %s %s %s %s %s',
code, clientInfo(client), redirectURI, userInfo(user), scope);
models.authorizationCodes.save(code, client.id, redirectURI, user.id,
scope,
function(err) {
done(err, err ? null : code);
function generateAuthCode() {
var code = generateToken({
grant: 'Authorization Code',
client: client,
user: user,
scope: scope,
redirectURI: redirectURI
});
debug('Generating authorization code: %s %s %s %s %s',
code, clientInfo(client), redirectURI, userInfo(user), scope);
models.authorizationCodes.save(code, client.id, redirectURI,
user.id,
scope,
function(err) {
done(err, err ? null : code);
});
}
if (ares.authorized) {
generateAuthCode();
} else {
models.permissions.addPermission(client.id, user.id, scope,
function(err) {
if (err) {
return done(err);
}
generateAuthCode();
});
}
}));

@@ -392,2 +253,9 @@

if (validateClient(client, {
scope: scope,
grantType: 'password'
}, done)) {
return;
}
userLogin(username, password, function(err, user) {

@@ -432,25 +300,62 @@ if (err || !user) {

server.exchange(oauth2Provider.exchange.clientCredentials(
function(client, scope, done) {
var token = generateToken({
grant: 'Client Credentials',
client: client,
scope: scope
});
debug('Generating access token: %s %s %s',
token, clientInfo(client), scope);
function(client, subject, scope, done) {
var refreshToken = generateToken({
grant: 'Client Credentials',
client: client,
scope: scope
});
if (validateClient(client, {
scope: scope,
grantType: 'client_credentials'
}, done)) {
return;
}
models.accessTokens.save(token, client.id, null, scope, refreshToken,
function(err, accessToken) {
done(err, err ? null : token, {
refresh_token: refreshToken,
expires_in: accessToken.expiresIn,
scope: scope && scope.join(' ')
function generateAccessToken(userId) {
var token = generateToken({
grant: 'Client Credentials',
client: client,
userId: userId,
scope: scope
});
debug('Generating access token: %s %s %s',
token, clientInfo(client), scope);
var refreshToken = generateToken({
grant: 'Client Credentials',
client: client,
scope: scope
});
models.accessTokens.save(token, client.id, userId, scope, refreshToken,
function(err, accessToken) {
done(err, err ? null : token, {
refresh_token: refreshToken,
expires_in: accessToken.expiresIn,
scope: scope && scope.join(' ')
});
});
}
if (subject) {
models.users.findByUsernameOrEmail(subject, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(new AuthorizationError(
'Invalid subject: ' + subject, 'access_denied'));
}
models.permissions.isAuthorized(client.id, user.id, scope,
function(err, authorized) {
if (err) {
return done(err);
}
if (authorized) {
generateAccessToken(user.id);
} else {
return done(new AuthorizationError(
'Permission denied by ' + subject, 'access_denied'));
}
});
});
} else {
generateAccessToken();
}
}));

@@ -465,2 +370,10 @@ }

function(client, refreshToken, scope, done) {
if (validateClient(client, {
scope: scope,
grantType: 'refresh_token'
}, done)) {
return;
}
models.accessTokens.findByRefreshToken(refreshToken,

@@ -516,19 +429,42 @@ function(err, accessToken) {

var tokenGrant;
if (supportedGrantTypes.indexOf('implicit') !== -1) {
server.grant(oauth2Provider.grant.token(
tokenGrant = server.grant(oauth2Provider.grant.token(
{ allowsPost: options.allowsPostForAuthorization},
function(client, user, scope, ares, done) {
var token = generateToken({
grant: 'Implicit',
client: client,
user: user,
scope: scope
});
debug('Generating access token: %s %s %s %s',
token, clientInfo(client), userInfo(user), scope);
models.accessTokens.save(token, client.id, user.id, scope, null,
function(err) {
done(err, err ? null : token);
if (validateClient(client, {
scope: scope,
grantType: 'implicit'
}, done)) {
return;
}
function generateAccessToken() {
var token = generateToken({
grant: 'Implicit',
client: client,
user: user,
scope: scope
});
debug('Generating access token: %s %s %s %s',
token, clientInfo(client), userInfo(user), scope);
models.accessTokens.save(token, client.id, user.id, scope, null,
function(err) {
done(err, err ? null : token);
});
}
if (ares.authorized) {
generateAccessToken();
} else {
models.permissions.addPermission(client.id, user.id, scope,
function(err) {
if (err) {
return done(err);
}
generateAccessToken();
});
}
}));

@@ -558,16 +494,50 @@ }

var payload = JSON.parse(decodedJWT.payload);
// payload.iss == client.id
var token = generateToken({
grant: 'JWT',
client: client,
claims: payload
});
debug('Generating access token %s %s %s', token,
clientInfo(client), jwtToken);
// FIXME: [rfeng] Map payload.sub to userId
// Check OAuthPermission model to see if it's pre-approved
models.accessTokens.save(token, client.id, null, payload.scope, null,
function(err) {
done(err, err ? null : token);
if (validateClient(client, {
scope: payload.scope,
grantType: 'urn:ietf:params:oauth:grant-type:jwt-bearer'
}, done)) {
return;
}
function generateAccessToken(userId) {
var token = generateToken({
grant: 'JWT',
client: client,
claims: payload
});
debug('Generating access token %s %s %s', token,
clientInfo(client), jwtToken);
// Check OAuthPermission model to see if it's pre-approved
models.accessTokens.save(token, client.id, userId, payload.scope, null,
function(err) {
done(err, err ? null : token);
});
}
if (payload.sub) {
models.users.findByUsernameOrEmail(payload.sub, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(new AuthorizationError(
'Invalid subject: ' + payload.sub, 'access_denied'));
}
models.permissions.isAuthorized(client.id, user.id, payload.scope,
function(err, authorized) {
if (err) {
return done(err);
}
if (authorized) {
generateAccessToken(user.id);
} else {
done(new AuthorizationError(
'Permission denied by ' + payload.sub), 'access_denied');
}
});
});
} else {
generateAccessToken();
}
}));

@@ -594,37 +564,52 @@ }

handlers.authorization = [
server.authorization(function(clientID, redirectURI, done) {
debug('Verifying client %s %s', clientID, redirectURI);
models.clients.findByClientId(clientID, function(err, client) {
if (err || !client) {
return done(err);
}
debug('Client found: %s', clientInfo(client));
var redirectURIs = [];
if (typeof client.redirectURI === 'string') {
redirectURIs.push(client.redirectURI);
}
if (Array.isArray(client.redirectURIs)) {
redirectURIs = redirectURIs.concat(client.redirectURIs);
}
debug('Checking redirect URIs %j', redirectURIs);
if (redirectURIs.length === 0) {
return done(null, client, redirectURI);
} else {
var matched = false;
for (var i = 0, n = redirectURIs.length; i < n; i++) {
if (redirectURI.indexOf(redirectURIs[i]) === 0) {
matched = true;
break;
}
}
if (!matched) {
err = new Error('Invalid redirectURI: ' + redirectURI);
server.authorization(
function(clientID, redirectURI, scope, responseType, done) {
debug('Verifying client %s redirect-uri: %s scope: %s response-type: %s',
clientID, redirectURI, scope, responseType);
models.clients.findByClientId(clientID, function(err, client) {
if (err || !client) {
return done(err);
}
debug('Client found: %s', clientInfo(client));
if (validateClient(client, {
scope: scope,
redirectURI: redirectURI,
responseType: responseType
}, done)) {
return;
}
return done(null, client, redirectURI);
}
});
}),
});
}),
// Ensure the user is logged in
login.ensureLoggedIn({ redirectTo: options.loginPage || '/login' }),
// Check if the user has granted permissions to the client app
function(req, res, next) {
if (options.forceAuthorize) {
return next();
}
var userId = req.oauth2.user.id;
var clientId = req.oauth2.client.id;
var scope = req.oauth2.req.scope;
models.permissions.isAuthorized(clientId, userId, scope,
function(err, authorized) {
if (err) {
return next(err);
} else if (authorized) {
req.oauth2.res = {};
req.oauth2.res.allow = true;
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 {
next();
}
});
},
// Now try to render the dialog to approve client app's request for permissions
function(req, res, next) {
if (options.decisionPage) {

@@ -717,8 +702,8 @@ var urlObj = {

var oauth2Paths = [
options.authorizePath || '/oauth/authorize',
options.tokenPath || '/oauth/token',
options.decisionPath || '/oauth/authorize/decision',
options.loginPath || '/login'
options.authorizePath || '/oauth/authorize',
options.tokenPath || '/oauth/token',
options.decisionPath || '/oauth/authorize/decision',
options.loginPath || '/login'
];
app.middleware('parse', oauth2Paths,
app.middleware('parse', oauth2Paths,
app.loopback.urlencoded({extended: false}));

@@ -725,0 +710,0 @@ app.middleware('parse', oauth2Paths, app.loopback.json({strict: false}));

@@ -69,4 +69,5 @@ /**

*/
exports.OAuth2Error = require('./errors/oauth2error');
exports.AuthorizationError = require('./errors/authorizationerror');
exports.TokenError = require('./errors/tokenerror');
var debug = require('debug')('loopback:oauth2:scope');
var oauth2Provider = require('./oauth2orize');
var helpers = require('./oauth2-helper');
/**
* Normalize scope to string[]
* @param {String|String[]} scope
* @returns {String[]}
*/
function normalizeScope(scope) {
if (!scope) {
return [];
}
var scopes;
if (Array.isArray(scope)) {
scopes = [].concat(scope);
} else if (typeof scope === 'string') {
scopes = scope.split(/[\s,]+/g).filter(Boolean);
} else {
throw new Error('Invalid scope: ' + scope);
}
return scopes;
}
/**
* Check if one of the scopes is in the requiredScopes array
* @param {String[]} requiredScopes An array of required scopes
* @param {String[]} scopes An array of granted scopes
* @returns {boolean}
*/
function isInScope(requiredScopes, scopes) {
if (requiredScopes.length === 0) {
return true;
}
for (var i = 0, n = requiredScopes.length; i < n; i++) {
if (requiredScopes.indexOf(scopes[i]) !== -1) {
return true;
}
}
return false;
}
function checkScopes(scopes, cb) {
cb(null, true);
}
module.exports = function(scope) {
var requiredScopes = normalizeScope(scope);
var allowedScopes = scope;
return function validateScope(req, res, next) {
debug('Required scopes: ', requiredScopes);
var scopes = normalizeScope(req.accessToken && req.accessToken.scopes);
debug('Allowed scopes: ', allowedScopes);
var scopes = req.accessToken && req.accessToken.scopes;
debug('Scopes of the access token: ', scopes);
if (isInScope(requiredScopes, scopes)) {
if (helpers.isScopeAllowed(allowedScopes, scopes)) {
next();

@@ -60,2 +19,2 @@ } else {

};
}
}

@@ -183,9 +183,10 @@ // Folked from

arity = self._verify.length;
if (arity === 4) {
if (arity === 5) {
// This variation allows the application to detect the case in which
// the issuer and subject of the assertion are different, and permit
// or deny as necessary.
self._verify(req, payload.iss || header.iss, header, verified);
} else { // arity == 3
self._verify(req, payload.iss || header.iss, verified);
self._verify(req, payload.iss || header.iss, payload.sub, payload,
verified);
} else { // arity == 4
self._verify(req, payload.iss || header.iss, payload.sub, verified);
}

@@ -192,0 +193,0 @@ } else {

{
"name": "loopback-component-oauth2",
"version": "2.0.0-beta4",
"version": "2.0.0-beta5",
"description": "OAuth 2.0 provider for LoopBack",

@@ -29,4 +29,4 @@ "keywords": [

"connect-ensure-login": "^0.1.1",
"debug": "^2.1.0",
"jws": "^1.0.0",
"debug": "^2.1.2",
"jws": "^2.0.0",
"passport": "^0.2.1",

@@ -43,4 +43,4 @@ "passport-http": "^0.2.2",

"devDependencies": {
"mocha": "^2.0.1",
"chai": "^1.10.0",
"mocha": "^2.1.0",
"chai": "^2.1.0",
"chai-connect-middleware": "^0.3.1",

@@ -47,0 +47,0 @@ "chai-oauth2orize-grant": "^0.2.0",

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc