74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
import { Request } from 'express';
|
|
|
|
import { Forbidden, Unauthorized } from './errors';
|
|
import { Optional } from '~/types';
|
|
import { RoleName, User, UserSession } from '~/models';
|
|
import Route from './routes/Route';
|
|
|
|
class AuthInfo {
|
|
public user: User;
|
|
public session: Optional<UserSession>;
|
|
|
|
constructor(user: User, session: Optional<UserSession> = null) {
|
|
this.user = user;
|
|
this.session = session;
|
|
}
|
|
}
|
|
|
|
function authenticate(roles: RoleName[] = []) {
|
|
return function (route: any, method: string) {
|
|
const routeClass = (<typeof Route> route.constructor);
|
|
routeClass.preRequestHandlers[method] = async (req: Request): Promise<AuthInfo> => {
|
|
let user: Optional<User>;
|
|
let session: Optional<UserSession>;
|
|
|
|
// Check the `session` cookie or the `Authorization` header for the session token
|
|
let token = req.cookies?.session;
|
|
if (!token?.length) {
|
|
const authHeader = req.headers?.authorization;
|
|
if (authHeader?.startsWith('Bearer ')) {
|
|
token = authHeader.slice(7);
|
|
}
|
|
}
|
|
|
|
// Check if the token is valid
|
|
if (token?.length) {
|
|
session = await $repos.userSessions.byToken(token);
|
|
if (session) {
|
|
user = await $repos.users.get(session.userId);
|
|
}
|
|
}
|
|
|
|
if (!(session && user)) {
|
|
// Check the `username` and `password` query or body parameters
|
|
const username = req.body?.username || req.query?.username;
|
|
const password = req.body?.password || req.query?.password;
|
|
|
|
if (username?.length && password?.length) {
|
|
user = await $repos.users.find(username);
|
|
if (!(user && user.checkPassword(password))) {
|
|
user = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!user) {
|
|
throw new Unauthorized('Invalid credentials');
|
|
}
|
|
|
|
if (roles.length) {
|
|
// Check if the user has the required roles
|
|
const userRoles = new Set((await user.roles()).map((role) => role.name));
|
|
const missingPermissions = roles.filter((role) => !userRoles.has(role));
|
|
|
|
if (missingPermissions.length) {
|
|
throw new Forbidden('Missing required roles: [' + missingPermissions.join(', ') + ']');
|
|
}
|
|
}
|
|
|
|
return new AuthInfo(user, session);
|
|
}
|
|
}
|
|
}
|
|
|
|
export { AuthInfo, authenticate };
|