'use strict'; exports = module.exports = { passwordAuth: passwordAuth, tokenAuth: tokenAuth, scope: scope, websocketAuth: websocketAuth }; var accesscontrol = require('../accesscontrol.js'), assert = require('assert'), BoxError = require('../boxerror.js'), externalLdap = require('../externalldap.js'), HttpError = require('connect-lastmile').HttpError, users = require('../users.js'), speakeasy = require('speakeasy'); function passwordAuth(req, res, next) { assert.strictEqual(typeof req.body, 'object'); 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; 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(); } 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)); check2FA(result); }); } 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')); check2FA(result); }); } 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')); check2FA(result); }); } } 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]; 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(); }); } // 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'); // function scope(requiredScope) { assert.strictEqual(typeof requiredScope, 'string'); var requiredScopes = requiredScope.split(','); return function (req, res, next) { assert(req.authInfo && typeof req.authInfo === 'object'); var error = accesscontrol.hasScopes(req.authInfo.authorizedScopes, requiredScopes); if (error) return next(new HttpError(403, error.message)); next(); }; } function websocketAuth(requiredScopes, req, res, next) { assert(Array.isArray(requiredScopes)); if (typeof req.query.access_token !== 'string') return next(new HttpError(401, 'Unauthorized')); accesscontrol.validateToken(req.query.access_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; var e = accesscontrol.hasScopes(info.authorizedScopes, requiredScopes); if (e) return next(new HttpError(403, e.message)); next(); }); }