authenticator

This commit is contained in:
Erik 2022-11-09 11:20:44 +02:00
parent 52bd21434b
commit dcec17599a
Signed by: Navy.gif
GPG Key ID: 811EC0CD80E7E5FB
2 changed files with 126 additions and 0 deletions

View File

@ -0,0 +1,123 @@
const { AbstractUserDatabase } = require('../interfaces');
const { Util } = require('../../util');
const session = require('express-session');
const MongoStore = require('connect-mongo');
const Strategy = require('@navy.gif/passport-discord');
const Passport = require('passport');
/**
* Takes care of sessions and authentication
*
* @class Authenticator
*/
class Authenticator {
/**
* Creates an instance of Authenticator.
* @param {Object} express
* @param {AbstractUserDatabase} users
* @param {Object} sessionOptions {
* mongo,
* cookie: {
* maxAge = 0.5 * 24 * 60 * 60 * 1000,
* secure = false
* },
* secret
* }
* @memberof Authenticator
*/
constructor (server, express, users, {
mongo, secret, discordID, discordSecret, callbackURL, discordScope, discordVersion,
cookie = { }
}) {
if (!(users instanceof AbstractUserDatabase)) Util.fatal(new Error(`Expecting user database to be an instance inheriting AbstractUserDatabase`));
this.userdb = users;
if (!mongo) Util.fatal(new Error('Missing mongo client for '));
this.logger = server.createLogger(this);
cookie = { maxAge: 0.5 * 24 * 60 * 60 * 1000, secure: false, ...cookie };
cookie.secure = cookie.secure && process.env.NODE_ENV !== 'development';
express.use(session({
cookie,
store: MongoStore.create({ client: mongo, dbName: 'sessions' }),
secret,
resave: false,
saveUninitialized: false
}));
express.use(Passport.initialize());
express.use(Passport.session());
Passport.serializeUser((user, callback) => {
callback(null, user.id);
});
Passport.deserializeUser(async (id, callback) => {
const user = await this.userdb.fetchUser(id);
callback(null, user);
});
Passport.use(new Strategy({
clientID: discordID, clientSecret: discordSecret, callbackURL, scope: discordScope, version: discordVersion
}, async (accessToken, refreshToken, profile, callback) => {
this.logger.info(`${profile.username} (${profile.id}) is logging in.`);
const user = await this.userdb.userFromDiscord(profile);
callback(null, user);
}));
}
async authenticate (req, res, next) {
if (this._authenticate(req, res)) return next();
}
async _authenticate (req, res) {
if (req.isAuthenticated()) return true;
// Sometimes the authorisation key automatically has a token type prepended to the header value,
// we don't care about that, we just want the key, which will always be the last element
const authHeader = req.get('Authorization') || req.get('Authorisation');
const segments = authHeader.split(' ');
const key = segments[segments.length - 1];
const user = await this.userdb.matchToken(key);
if (user) req.user = user;
else {
res.status(401).send('Unknown identity');
return false;
}
return true;
}
/**
* Authorisation implicitly checks for authentication
*
* @param {*} permission
* @return {*}
* @memberof Authenticator
*/
createAuthoriser (permission) {
const func = (req, res, next) => {
const { user } = req;
if (!this._authenticate(req, res)) return;
if (user.hasPermission(permission)) return next();
return res.status(403).send('Access denied');
};
return func;
}
}
module.exports = Authenticator;

View File

@ -0,0 +1,3 @@
module.exports = {
Authenticator: require('./Authenticator')
};