Middleware para Método de Autenticação Basic Auth do HTTP
É uma forma de autenticação baseada no protocolo HTTP.
O objetivo é reaproveitar o método, ao criar um Middleware podemos invocar ele em vários outros endpoints.
Extender o @types/express
Extender o @types/express incluindo o type User no Request para poder propagar o usuário para o próximo middleware.Criar um arquivo de definição para o express src/@types/express.d.ts, é uma espécie de "interface".
import { User } from '../models/user.model';
declare module 'express-serve-static-core' {
interface Request {
user?: User | null;
}
}
Isso pode ser feito para incluir qualquer tipo de atributo customizado.
Criar o Middleware
Criar o Middleware em src/middlewares/basic-authentication.tsColocar o user dentro da requisição para ficar acessível em todas as rotas.
Chamar o next() sem passar erro para que ele continue propagando entre os middlewares e as rotas.
Se passar algum erro ele irá para o middleware de Error Handler em src/middlewares/error-handler.ts
import { Request, Response, NextFunction } from 'express';
import ForbiddenError from '../models/errors/forbidden.error.model';
import userRepository from '../repositories/user.repository';
async function basicAuthentication(req: Request, res: Response, next: NextFunction){
try{
// Recuperar usuário e senha do header da requisição
const authorizationHeader = req.headers['authorization'];
if (!authorizationHeader) {
throw new ForbiddenError('Credenciais não informadas');
}
const [authorizationType, token] = authorizationHeader?.split(" ");
if(authorizationType !== 'Basic' || !token){
throw new ForbiddenError('Tipo de autenticação inválida');
}
// Criar buffer a partir do Token que está em Base64 e converter para string codificação UTF-8
const tokenContent = Buffer.from(token, 'base64').toString('utf-8');
const [userName, userPassword] = tokenContent.split(":");
if(!userName || !userPassword){
throw new ForbiddenError('Credenciais não preenchidas');
}
// Obter usuário e senha da Database
const user = await userRepository.byUserNameAndPassword(userName, userPassword);
if(!user){
throw new ForbiddenError('Usuário e/ou senha inválidos');
}
// Colocar o user dentro da requisição
req.user = user;
next();
}catch(error){
next(error);
}
};
export default basicAuthentication;
Configurar o Middleware no Server
Neste caso, NÃO vamos configurar o middleware no server src/server.ts pois não queremos que ele seja global, mas que seja usado somente na rota de autenticação, portanto local.Configurar o Middleware na rota de autenticação
Configurar o Middleware basicAuthentication na rota de autorização em src/routes/authorization.route.tsRecuperar o user do request.
import { Router, Request, Response, NextFunction } from 'express';
import basicAuthentication from '../middlewares/basic-authentication';
import JWT from 'jsonwebtoken';
import { StatusCodes } from 'http-status-codes';
import ForbiddenError from '../models/errors/forbidden.error.model';
const authorizationRouter = Router();
authorizationRouter.post('/token', basicAuthentication, async (req: Request, res: Response, next: NextFunction) => {
try{
// Recuperar o user da request
const user = req.user;
if(!user){
throw new ForbiddenError('Usuário não informado!');
}
// Gerar token JWT
const jwtPayLoad = {userName: user.userName};
const jwtSecretKey = process.env.JWT_SECRET_KEY;
const jwtOptions = {subject: user?.uuid};
const jwtToken = JWT.sign(jwtPayLoad, jwtSecretKey, jwtOptions);
// Response Token
res.status(StatusCodes.OK).json({ token: jwtToken });
}catch(error){
next(error);
}
});
export default authorizationRouter;