blob: 71eda249b4930b9d0f36c5f614e03b3fb64b35ff [file] [log] [blame]
var http = require('http');
var express = require('express');
var validator = require('express-validator');
var cookieSession = require('cookie-session');
var bodyParser = require('body-parser');
var csrf = require('csurf');
var Promise = require('bluebird');
var basicAuth = require('basic-auth');
var request = require('request');
var logger = require('../../util/logger');
var requtil = require('../../util/requtil');
var jwtutil = require('../../util/jwtutil');
var pathutil = require('../../util/pathutil');
var urlutil = require('../../util/urlutil');
var lifecycle = require('../../util/lifecycle');
var dbapi = require('../../db/api');
var config = require('../../../config');
const JWT_EXPIRE_LENGTH = 24 * 3600;
const DEFAULT_EXPIRE_MINS = 5.0;
const MILLIS_IN_ONE_MINUTE = 60 * 1000;
module.exports = function(options) {
var log = logger.createLogger('auth-token');
var app = express();
var server = Promise.promisifyAll(http.createServer(app));
// The auth module usually redirects, but use jade to display configurable
// logout & session end messaging.
app.set('view engine', 'jade');
app.set('views', pathutil.resource('app/views'));
lifecycle.observe(function() {
log.info('Waiting for client connections to end');
return server.closeAsync();
});
var basicAuthMiddleware = function(req, res, next) {
function unauthorized(res) {
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
return res.send(401);
}
var user = basicAuth(req);
if (!user || !user.name || !user.pass) {
return unauthorized(res);
}
if (user.name === options.mock.basicAuth.username &&
user.pass === options.mock.basicAuth.password
) {
return next();
}
else {
return unauthorized(res);
}
};
app.set('strict routing', true);
app.set('case sensitive routing', true);
app.use(cookieSession({
name: options.ssid,
keys: [options.secret]
}));
app.use(bodyParser.json());
app.use(csrf());
app.use(validator());
app.use(function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
if (options.mock.useBasicAuth) {
app.use(basicAuthMiddleware);
}
var resetSession = function(req, res) {
res.clearCookie('XSRF-TOKEN');
res.clearCookie('ssid');
res.clearCookie('ssid.sig');
// Show token expiration messaging.
res.render('taskend', {contactEmail: config.hitAccounts.contactEmail});
};
app.get('/auth/token', resetSession);
app.get('/auth/token/', resetSession);
app.get('/auth/token/:token', function(req, res) {
res.clearCookie('XSRF-TOKEN');
res.clearCookie('ssid');
res.clearCookie('ssid.sig');
var token = req.params.token;
if (token) {
// Check if token is in db and is valid.
dbapi.getToken(token).then(function(tokenObj) {
if (tokenObj && tokenObj.status === 'unused') {
var log = logger.createLogger('auth-token');
log.setLocalIdentifier(req.ip);
log.info('Authenticated token "%s"', tokenObj.token);
var jwtOptions = {
payload: {
email: token,
name: 'token'
},
secret: options.secret,
header: {
exp: Date.now() + JWT_EXPIRE_LENGTH
}
};
var jwtToken = jwtutil.encode(jwtOptions);
var authRedirURL = urlutil.addParams(options.appUrl, {
jwt: jwtToken,
serial: tokenObj.serial
});
tokenObj.jwtToken = jwtToken;
tokenObj.jwtOptions = jwtOptions;
tokenObj.status = 'active';
tokenObj.activeTimeStart = Date.now();
tokenObj.activeIP = req.ip;
if (tokenObj.expireMinutes) {
tokenObj.expireMinutes = parseFloat(tokenObj.expireMinutes);
} else {
tokenObj.expireMinutes = DEFAULT_EXPIRE_MINS;
}
setTimeout(function() {
console.log('Kicking user, reached timeout for token',
tokenObj.token);
request.delete(options.appUrl + '/app/api/v1/token/' +
tokenObj.token + '?authed=true',
function(err, res) {
if (err) {
log.error('Error kicking user token ', tokenObj.token,
err.stack);
}
});
}, tokenObj.expireMinutes * MILLIS_IN_ONE_MINUTE);
dbapi.updateToken(tokenObj).then(function() {
log.info('Marked token "%s" active', tokenObj.token);
});
log.info('Issuing auth redirect to "%s"', authRedirURL);
res.redirect(authRedirURL);
}
else {
return resetSession(req, res);
}
}).catch(function(err) {
log.error('Failed to load token "%s": ', token, err.stack);
return res.redirect('/auth/token');
});
} else {
return res.status(400).json({
success: false,
error: 'ValidationError',
validationErrors: ['No token provided']
});
}
});
app.get('/auth/token-google', function(req, res) {
res.clearCookie('XSRF-TOKEN');
res.clearCookie('ssid');
res.clearCookie('ssid.sig');
var log = logger.createLogger('auth-token');
log.setLocalIdentifier(req.ip);
log.info('Authenticated Admin Token Google');
var jwtOptions = {
payload: {
email: 'google@google.com',
name: 'google'
},
secret: options.secret,
header: {
exp: Date.now() + 24 * 3600
}
};
var jwtToken = jwtutil.encode(jwtOptions);
var authRedirURL = urlutil.addParams(options.appUrl + '#!/devices-home', {
jwt: jwtToken
});
log.info('Issuing auth redirect to "%s"', authRedirURL);
res.redirect(authRedirURL);
});
server.listen(options.port);
log.info('Listening on port %d', options.port);
};