game flow revamp
This commit is contained in:
213
src/server/controllers/AuthController.ts
Normal file
213
src/server/controllers/AuthController.ts
Normal file
@ -0,0 +1,213 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { UsersMongoManager } from "../db/mongo/UsersMongoManager";
|
||||
import { SecurityManager } from '../managers/SecurityManager';
|
||||
import { ApiTokenMongoManager } from '../db/mongo/ApiTokenMongoManager';
|
||||
import { TemporalTokenMongoManager } from '../db/mongo/TemporalTokenMongoManager';
|
||||
import { BaseController } from './BaseController';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { AuthenticationOption, Token, User } from '../db/interfaces';
|
||||
|
||||
export class AuthController extends BaseController {
|
||||
security = new SecurityManager();
|
||||
usersManager = new UsersMongoManager();
|
||||
temporalTokenManager = new TemporalTokenMongoManager();
|
||||
|
||||
async login(req: Request, res: Response): Promise<void> {
|
||||
const { log } = req;
|
||||
try {
|
||||
let token = null
|
||||
const { username, password } = req.body;
|
||||
this.logger.debug('login', username, password);
|
||||
const { valid: isValidPassword, user } = await this._checkPassword(username, password);
|
||||
this.logger.debug('isValidPassword', isValidPassword);
|
||||
if (!isValidPassword) {
|
||||
res.status(401).json({ error: 'Unauthorized' }).end();
|
||||
log.error('Unauthorized login attempt for user: ', username);
|
||||
return;
|
||||
}
|
||||
this._jwtSignUser(user, res)
|
||||
} catch (error) {
|
||||
this.handleError(res, error);
|
||||
}
|
||||
}
|
||||
|
||||
_jwtSignUser(user: User | null, res: Response) {
|
||||
if (user === null) {
|
||||
res.status(401).json({ error: 'Unauthorized' }).end();
|
||||
return;
|
||||
}
|
||||
delete user.hash;
|
||||
const token = this.security.signJwt(user);
|
||||
if (token === null) {
|
||||
res.status(401).json({ error: 'Unauthorized' }).end();
|
||||
} else {
|
||||
res.status(200).json({ token }).end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async twoFactorCodeAuthentication(req: Request, res: Response) {
|
||||
const { code, username } = req.body;
|
||||
const { valid: isValid, user } = await this._isValidTemporalCode(username, code);
|
||||
if (!isValid) {
|
||||
res.status(406).json({ error: 'Unauthorized' }).end();
|
||||
return;
|
||||
}
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
async _isValidTemporalCode(username: string, code: string) {
|
||||
const user = await this.usersManager.getByUsername(username);
|
||||
if (user === null || user._id === undefined) {
|
||||
return { valid: false, user: null };
|
||||
}
|
||||
const temporalToken = await this.temporalTokenManager.getByUserAndType(user._id.toString(), TemporalTokenMongoManager.Types.PASSWORD_RECOVERY);
|
||||
if (temporalToken === null) {
|
||||
return { valid: false, user: null };
|
||||
}
|
||||
|
||||
const { token } = temporalToken;
|
||||
const valid = bcrypt.compareSync(code, token);
|
||||
return { valid, user: valid ? user : null};
|
||||
}
|
||||
|
||||
async changePasswordWithCode(req: Request, res: Response) {
|
||||
try {
|
||||
const { username, newPassword, code } = req.body;
|
||||
const { valid: isValid, user } = await this._isValidTemporalCode(username, code);
|
||||
if (isValid) {
|
||||
await this._setNewPassword(username, newPassword);
|
||||
this._jwtSignUser(user, res);
|
||||
} else {
|
||||
res.status(400).json({ error: 'Code not valid.' }).end();
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleError(res, error);
|
||||
}
|
||||
}
|
||||
|
||||
async changePassword(req: Request, res: Response) {
|
||||
try {
|
||||
const { username, oldPassword, newPassword } = req.body;
|
||||
const { valid: isValidPassword } = await this._checkPassword(username, oldPassword);
|
||||
if (isValidPassword) {
|
||||
await this._setNewPassword(username, newPassword);
|
||||
res.status(200).end();
|
||||
}
|
||||
res.status(400).json({ error: 'Password not valid.' }).end();
|
||||
} catch (error) {
|
||||
this.handleError(res, error);
|
||||
}
|
||||
}
|
||||
|
||||
async _setNewPassword(username: string, newPassword: string) {
|
||||
const hash = this.security.getHashedPassword(newPassword);
|
||||
await this.usersManager.updatePassword(username, hash);
|
||||
}
|
||||
|
||||
async _checkPassword(username: string, password: string) {
|
||||
let valid = false;
|
||||
const user = await this.usersManager.getByUsername(username);
|
||||
if (user && user.hash) {
|
||||
const { hash } = user;
|
||||
valid = bcrypt.compareSync(password, hash);
|
||||
}
|
||||
return { valid, user };
|
||||
}
|
||||
|
||||
static async checkRolesToken(token: Token, rolesToCheck: string[]) {
|
||||
if (rolesToCheck.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!token._id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tokenFromDb = await new ApiTokenMongoManager().getById(token._id.toString());
|
||||
|
||||
if (!tokenFromDb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { roles } = tokenFromDb;
|
||||
const validRoles = rolesToCheck.filter((r: string) => roles.includes(r));
|
||||
return validRoles.length === rolesToCheck.length;
|
||||
}
|
||||
|
||||
static async checkRoles(user: User, rolesToCheck: string[]) {
|
||||
if (rolesToCheck.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!user._id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const usersManager = new UsersMongoManager();
|
||||
const userFromDb = await usersManager.getById(user._id.toString());
|
||||
|
||||
if (!userFromDb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { roles } = userFromDb;
|
||||
const validRoles = rolesToCheck.filter((r: string) => roles.includes(r));
|
||||
return validRoles.length === rolesToCheck.length;
|
||||
}
|
||||
|
||||
static authenticate(options: AuthenticationOption = {}) {
|
||||
return async function(req: Request, res: Response, next: NextFunction) {
|
||||
const security = new SecurityManager();
|
||||
const token = req.headers.authorization;
|
||||
const { roles = [] } = options;
|
||||
if (!token) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
try {
|
||||
const user: User = await security.verifyJwt(token);
|
||||
const validRoles = await AuthController.checkRoles(user, roles);
|
||||
if (!validRoles) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
req.user = user;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static tokenAuthenticate(options: AuthenticationOption = {}) {
|
||||
return async function(req: Request, res: Response, next: NextFunction) {
|
||||
const { log } = req;
|
||||
// log.info('tokenAuthenticate')
|
||||
try {
|
||||
const token: string = req.headers['x-api-key'] as string;
|
||||
const dm = new ApiTokenMongoManager();
|
||||
const apiToken = await dm.getByToken(token);
|
||||
const { roles = [] } = options;
|
||||
const valid = !!apiToken && await AuthController.checkRolesToken(apiToken, roles);
|
||||
if (!valid) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
req.token = apiToken;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async withUser(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const token = req.token;
|
||||
const dm = new UsersMongoManager();
|
||||
const user = await dm.getById(token.userId);
|
||||
req.user = user;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user