This is the template project that's checked out and configured when you run the bando-up command from ljsthw-bandolier. This is where the code really lives.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bandolier-template/lib/auth.js

164 lines
4.8 KiB

import cookie from 'cookie';
import { Strategy } from 'passport-local';
import passport from 'passport';
import signature from 'cookie-signature';
import redis from 'redis';
import session from 'express-session';
import memorystore from 'memorystore';
import connectRedis from 'connect-redis';
import * as models from '../lib/models.js';
import logging from '../lib/logging.js';
import { session_store, auth } from "../lib/config.js";
import assert from "assert";
const log = logging.create("lib/auth.js");
const redisClient = redis.createClient();
const MemoryStore = memorystore(session);
const RedisStore = connectRedis(session);
passport.use(new Strategy(
async (username, password, cb) => {
let user = await models.User.auth(username, password);
return user ? cb(null, user) : cb(null, false);
})
);
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser(async (id, cb) => {
try {
const user = await models.User.first({id});
if(user) {
return cb(null, user);
} else {
return cb(null, false);
}
} catch(error) {
log.error(error);
return cb(null, false);
}
});
export const configure_sessions = () => {
if(session_store === "redis") {
log.info("Using redis to store sessions.");
return new RedisStore({client: redisClient});
} else if(session_store === "memory") {
log.warn("Using memory to store sessions. Restarts will force logins.");
return new MemoryStore({ checkPeriod: 86400000, });
} else {
assert(false, `Error, unknown session store ${session_store} in secrets/config.json. Only "redis" and "memory" supported.`);
return undefined; // shouldn't reach this but shut up eslint
}
}
export const sessionStore = configure_sessions();
export const cookie_secret = auth.cookie_secret;
export const cookie_domain = auth.cookie_domain;
assert(cookie_domain !== undefined, "secrets/config.json:auth.cookie_domain isn't set.");
export const login = () => {
return (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if(err) {
log.error(err, info);
return res.status(500).json({error: info});
} else if(!user) {
// log.debug(`Auth attempt user is ${user.id}.`);
return res.status(401).json({error: "Authentication required"});
} else {
// log.debug(`User exists, attempting login ${user.id}`);
return req.login(user, (err) => {
if(err) {
log.error(err);
res.status(403).json({error: "Invalid login"});
// BUG: is this right or is next("route")
return next(err);
} else {
return next();
}
});
}
})(req, res, next);
}
}
export const required = () => {
return (req, res, next) => {
const user = req.user;
if(!user) {
return res.status(401).json({error: "Authentication required"});
// WARNING: next('route') is NOT needed to abort the chain of calls despite express docs
} else {
return next();
}
}
}
export const socket = async (socket) => {
try {
if(!socket.handshake.headers.cookie) {
throw new Error("No cookies in socket handshake headers, disconnecting socket.");
}
let cookies = cookie.parse(socket.handshake.headers.cookie);
if(!cookies) {
throw new Error("No cookies were parsed even though the header exists, disconnecting socket.");
}
const connect_sid = cookies['connect.sid'];
if(!connect_sid) {
throw new Error("No connect.sid cookie in the list of cookies, disconnecting socket.");
}
if(connect_sid.slice(0, 2) !== 's:') {
throw new Error(`is_authenticate only supports signed cookies. Your cookies don't start with s: they start with ${connect_sid.slice(0, 2)}`);
}
let session_id = signature.unsign(connect_sid.slice(2), cookie_secret);
log.debug(`Session id is ${session_id}`);
if(!session_id) {
throw new Error(`Unable to get session_id ${session_id} with session signature.`);
}
return new Promise((resolve, reject) => {
sessionStore.get(session_id, (err, session) => {
socket.express_session = session;
if(err) {
log.error(err);
reject(err);
} else if(session && session.passport && session.passport.user !== undefined) {
socket.user_id = session.passport.user;
resolve(true);
} else {
socket.disconnect();
delete socket.user;
delete socket.express_session;
resolve(false);
}
});
});
} catch(error) {
// not logging the error stack trace for now since i'm using the Error as a jump/clean
log.debug(error.message);
socket.disconnect();
delete socket.user;
delete socket.express_session;
return false;
}
}
export const init = (app) => {
app.use(passport.initialize());
app.use(passport.session());
}