2018-04-26 15:54:53 -07:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
exports = module.exports = {
|
2020-02-06 14:50:12 +01:00
|
|
|
passwordAuth: passwordAuth,
|
|
|
|
|
tokenAuth: tokenAuth,
|
2018-08-03 17:21:24 -07:00
|
|
|
|
2018-04-26 15:54:53 -07:00
|
|
|
scope: scope,
|
2018-09-05 23:03:17 -07:00
|
|
|
websocketAuth: websocketAuth
|
2018-04-26 15:54:53 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var accesscontrol = require('../accesscontrol.js'),
|
|
|
|
|
assert = require('assert'),
|
2019-10-22 21:16:00 -07:00
|
|
|
BoxError = require('../boxerror.js'),
|
2019-11-20 19:48:40 +01:00
|
|
|
externalLdap = require('../externalldap.js'),
|
2018-04-26 15:54:53 -07:00
|
|
|
HttpError = require('connect-lastmile').HttpError,
|
2020-02-06 15:36:14 +01:00
|
|
|
users = require('../users.js'),
|
|
|
|
|
speakeasy = require('speakeasy');
|
2018-06-15 17:31:28 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
function passwordAuth(req, res, next) {
|
|
|
|
|
assert.strictEqual(typeof req.body, 'object');
|
2018-06-15 17:31:28 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
if (!req.body.username || typeof req.body.username !== 'string') return next(new HttpError(400, 'A username must be non-empty string'));
|
|
|
|
|
if (!req.body.password || typeof req.body.password !== 'string') return next(new HttpError(400, 'A password must be non-empty string'));
|
|
|
|
|
|
|
|
|
|
const username = req.body.username;
|
|
|
|
|
const password = req.body.password;
|
|
|
|
|
|
2020-02-06 15:36:14 +01:00
|
|
|
function check2FA(user) {
|
|
|
|
|
assert.strictEqual(typeof user, 'object');
|
|
|
|
|
|
|
|
|
|
if (!user.ghost && !user.appPassword && user.twoFactorAuthenticationEnabled) {
|
|
|
|
|
if (!req.body.totpToken) return next(new HttpError(401, 'A totpToken must be provided'));
|
|
|
|
|
|
|
|
|
|
let verified = speakeasy.totp.verify({ secret: user.twoFactorAuthenticationSecret, encoding: 'base32', token: req.body.totpToken, window: 2 });
|
|
|
|
|
if (!verified) return next(new HttpError(401, 'Invalid totpToken'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.user = user;
|
|
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
function createAndVerifyUserIfNotExist(identifier, password) {
|
|
|
|
|
assert.strictEqual(typeof identifier, 'string');
|
|
|
|
|
assert.strictEqual(typeof password, 'string');
|
|
|
|
|
|
|
|
|
|
externalLdap.createAndVerifyUserIfNotExist(identifier.toLowerCase(), password, function (error, result) {
|
|
|
|
|
if (error && error.reason === BoxError.BAD_STATE) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error && error.reason === BoxError.BAD_FIELD) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error && error.reason === BoxError.CONFLICT) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error && error.reason === BoxError.NOT_FOUND) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error) return next(new HttpError(500, error));
|
2018-06-15 17:31:28 -07:00
|
|
|
|
2020-02-06 15:36:14 +01:00
|
|
|
check2FA(result);
|
2018-06-15 17:31:28 -07:00
|
|
|
});
|
2020-02-06 14:50:12 +01:00
|
|
|
}
|
2018-06-15 17:31:28 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
if (username.indexOf('@') === -1) {
|
|
|
|
|
users.verifyWithUsername(username, password, users.AP_WEBADMIN, function (error, result) {
|
|
|
|
|
if (error && error.reason === BoxError.NOT_FOUND) return createAndVerifyUserIfNotExist(username, password);
|
|
|
|
|
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error) return next(new HttpError(500, error));
|
|
|
|
|
if (!result) return next(new HttpError(401, 'Unauthorized'));
|
2019-11-20 19:48:40 +01:00
|
|
|
|
2020-02-06 15:36:14 +01:00
|
|
|
check2FA(result);
|
2020-02-06 14:50:12 +01:00
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
users.verifyWithEmail(username, password, users.AP_WEBADMIN, function (error, result) {
|
|
|
|
|
if (error && error.reason === BoxError.NOT_FOUND) return createAndVerifyUserIfNotExist(username, password);
|
|
|
|
|
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
if (error) return next(new HttpError(500, error));
|
|
|
|
|
if (!result) return next(new HttpError(401, 'Unauthorized'));
|
2018-06-15 17:31:28 -07:00
|
|
|
|
2020-02-06 15:36:14 +01:00
|
|
|
check2FA(result);
|
2020-02-06 14:50:12 +01:00
|
|
|
});
|
|
|
|
|
}
|
2018-06-15 17:31:28 -07:00
|
|
|
}
|
|
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
function tokenAuth(req, res, next) {
|
|
|
|
|
var token;
|
|
|
|
|
|
|
|
|
|
// this determines the priority
|
|
|
|
|
if (req.body && req.body.access_token) token = req.body.access_token;
|
|
|
|
|
if (req.query && req.query.access_token) token = req.query.access_token;
|
|
|
|
|
if (req.headers && req.headers.authorization) {
|
|
|
|
|
var parts = req.headers.authorization.split(' ');
|
|
|
|
|
if (parts.length == 2) {
|
|
|
|
|
var scheme = parts[0];
|
|
|
|
|
var credentials = parts[1];
|
2018-06-15 17:31:28 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
if (/^Bearer$/i.test(scheme)) token = credentials;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!token) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
|
|
|
|
|
accesscontrol.validateToken(token, function (error, user, info) {
|
|
|
|
|
if (error) return next(new HttpError(500, error.message));
|
|
|
|
|
if (!user) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
|
|
|
|
|
req.user = user;
|
|
|
|
|
req.authInfo = info;
|
|
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
});
|
2018-06-15 17:31:28 -07:00
|
|
|
}
|
|
|
|
|
|
2018-04-26 15:54:53 -07:00
|
|
|
// The scope middleware provides an auth middleware for routes.
|
|
|
|
|
//
|
|
|
|
|
// It is used for API routes, which are authenticated using accesstokens.
|
|
|
|
|
// Those accesstokens carry OAuth scopes and the middleware takes the required
|
|
|
|
|
// scope as an argument and will verify the accesstoken against it.
|
|
|
|
|
//
|
|
|
|
|
// See server.js:
|
|
|
|
|
// var profileScope = routes.oauth2.scope('profile');
|
|
|
|
|
//
|
2018-06-14 16:32:24 -07:00
|
|
|
function scope(requiredScope) {
|
|
|
|
|
assert.strictEqual(typeof requiredScope, 'string');
|
2018-04-26 15:54:53 -07:00
|
|
|
|
2018-06-14 16:32:24 -07:00
|
|
|
var requiredScopes = requiredScope.split(',');
|
2018-04-26 15:54:53 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
return function (req, res, next) {
|
|
|
|
|
assert(req.authInfo && typeof req.authInfo === 'object');
|
2018-06-17 15:25:41 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
var error = accesscontrol.hasScopes(req.authInfo.authorizedScopes, requiredScopes);
|
|
|
|
|
if (error) return next(new HttpError(403, error.message));
|
2018-04-26 15:54:53 -07:00
|
|
|
|
2020-02-06 14:50:12 +01:00
|
|
|
next();
|
|
|
|
|
};
|
2018-04-26 15:54:53 -07:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 16:32:24 -07:00
|
|
|
function websocketAuth(requiredScopes, req, res, next) {
|
|
|
|
|
assert(Array.isArray(requiredScopes));
|
2018-04-26 15:54:53 -07:00
|
|
|
|
|
|
|
|
if (typeof req.query.access_token !== 'string') return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
|
2018-06-27 23:17:04 -07:00
|
|
|
accesscontrol.validateToken(req.query.access_token, function (error, user, info) {
|
2018-04-26 15:54:53 -07:00
|
|
|
if (error) return next(new HttpError(500, error.message));
|
|
|
|
|
if (!user) return next(new HttpError(401, 'Unauthorized'));
|
|
|
|
|
|
|
|
|
|
req.user = user;
|
|
|
|
|
|
2018-06-17 19:54:05 -07:00
|
|
|
var e = accesscontrol.hasScopes(info.authorizedScopes, requiredScopes);
|
2018-06-15 20:51:26 -07:00
|
|
|
if (e) return next(new HttpError(403, e.message));
|
2018-04-26 15:54:53 -07:00
|
|
|
|
|
|
|
|
next();
|
|
|
|
|
});
|
|
|
|
|
}
|