rolling session

This commit is contained in:
Erik 2023-05-09 14:20:15 +03:00
parent 68512ec871
commit 16016071a4
Signed by: Navy.gif
GPG Key ID: 2532FBBB61C65A68

View File

@ -17,7 +17,7 @@ import { MiddlewareFunction, Request, Response } from "../../../@types/Server.js
import { NextFunction } from "express";
import { User } from "../structures/index.js";
const OAuthLinks: {[key: string]: string} = {};
const OAuthLinks: { [key: string]: string } = {};
type Cookie = {
secure?: boolean,
@ -45,7 +45,7 @@ class Authenticator {
#passport: passport.Authenticator;
#logger: LoggerClient;
#OAuthParams: {[key: string]: OAuthParams | null};
#OAuthParams: { [key: string]: OAuthParams | null };
/**
* Creates an instance of Authenticator.
@ -64,19 +64,31 @@ class Authenticator {
constructor (
server: Server,
users: UserDatabaseInterface,
{ sessionStorage, secret, name, cookie = {} }: { sessionStorage: MongoStore, secret: string, name: string, cookie: Cookie }
{
rollingSession = true,
sessionStorage,
secret,
name,
cookie = {}
}: {
sessionStorage: MongoStore,
rollingSession?: boolean,
secret: string,
name: string,
cookie: Cookie
}
) {
// if (!(users instanceof UserDatabaseInterface))
// Util.fatal(new Error(`Expecting user database to be an instance inheriting UserDatabaseInterface`));
this.#_userdb = users;
if (!sessionStorage)
if (!sessionStorage)
Util.fatal(new Error('Missing session storage'));
this.#logger = server.createLogger(this);
this.#_cookieName = name;
cookie = { maxAge: 0.5 * 24 * 60 * 60 * 1000, secure: false, ...cookie };
cookie.secure = cookie.secure && process.env.NODE_ENV !== 'development';
server.app.use(session({
@ -85,7 +97,8 @@ class Authenticator {
store: sessionStorage,
secret,
resave: false,
saveUninitialized: false
saveUninitialized: false,
rolling: rollingSession
}));
this.#passport = passport;
@ -107,8 +120,8 @@ class Authenticator {
this.#OAuthParams = {};
server.OAuthProviders.forEach(provider => {
const providerName = provider.name.toLowerCase();
OAuthLinks[providerName] = provider.authoriseURL;
OAuthLinks[providerName +'Token'] = provider.tokenURL;
OAuthLinks[providerName] = provider.authoriseURL;
OAuthLinks[providerName + 'Token'] = provider.tokenURL;
});
}
@ -121,7 +134,7 @@ class Authenticator {
* @param {object} [{ failureRedirect = '/api/login/fail', successRedirect = '/home' }={}]
* @memberof Authenticator
*/
addStrategy (name: string, strategy: passport.Strategy, { failureRedirect = '/api/login/fail', successRedirect, authParams }: {failureRedirect?: string, successRedirect?: string, authParams?: OAuthParams } = {}) {
addStrategy (name: string, strategy: passport.Strategy, { failureRedirect = '/api/login/fail', successRedirect, authParams }: { failureRedirect?: string, successRedirect?: string, authParams?: OAuthParams } = {}) {
this.#logger.info(`Adding ${name} authentication strategy`);
this.#passport.use(name, strategy);
this.#OAuthParams[name] = authParams || null;
@ -161,31 +174,31 @@ class Authenticator {
// For API requests, does not redirect to a login page
async #_auth (req: Request, res: Response, next: NextFunction) {
if (await this.#_authenticate(req, res))
if (await this.#_authenticate(req, res))
return next();
}
// Meant for non-api paths
#_authenticateRedirect (req: Request, res: Response, next: NextFunction) {
// '/login' should point towards a login page, should probably make this configurable
if (!req.isAuthenticated())
if (!req.isAuthenticated())
return res.redirect('/login');
if (req.user.temporary)
if (req.user.temporary)
return res.redirect('/login/finalise');
next();
}
// Check if request is authenticated, meant for API requests
// Takes care of responding to unauthenticated requests
async #_authenticate (req: Request, res: Response) {
if (req.user) {
if (req.user.temporary) {
res.status(403).send('Temporary accounts must be finalised before proceeding');
res.status(403).send('Temporary accounts must be finalised before proceeding');
return false;
}
return true;
} else if (req.get('cookie')?.includes(this.#_cookieName)) {
res.status(401).send('Invalid session');
res.status(401).send('Invalid session');
return false;
}
@ -202,16 +215,16 @@ class Authenticator {
const application = await this.#_userdb.matchToken(key);
if (application) {
req.user = application;
req.user = application;
} else {
res.status(401).send('Unknown identity');
res.status(401).send('Unknown identity');
return false;
}
return true;
}
/**
* Authorisation implicitly checks for authentication
*
@ -221,17 +234,17 @@ class Authenticator {
* @memberof Authenticator
*/
createAuthoriser (permission: string, level = 1) {
PermissionManager.ensurePermission(permission);
const func = async (req: Request, res: Response, next: NextFunction) => {
// Request does not have a user bound to it, response already sent from #_authenticate
if (!await this.#_authenticate(req, res))
if (!await this.#_authenticate(req, res))
return;
// If the authentication is done through a token, the user will be attached in the authentication step
const { user } = req;
// Has permission
if (user.hasPermission(permission, level))
if (user.hasPermission(permission, level))
return next();
return res.status(403).send('Insufficient permissions');
};
@ -244,7 +257,7 @@ class Authenticator {
service = service.toLowerCase();
const link = OAuthLinks[service];
const _params = this.#OAuthParams[service];
if (!_params)
if (!_params)
return null;
const params = {
response_type: 'code',