Como validar o JWT utilizando um middleware no ExpressJS
O objetivo deste post é mostrar como podemos construir um middleware no Express para validar um JWT (Json Web Token) e propagar suas informações para as demais rotas.
Iniciando o projeto
mkdir jwt-middleware
cd jwt-middleware
npm i express jsonwebtoken body-parser
touch index.js
Dentro do arquivo index.js
vamos adicionar o básico para ver a aplicação em execução:
const express = require('express');
const app = express();
// Efetua o parse do application/json
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get(
"/status",
(req, res) => res.json({ status: "OK" })
);
app.listen(3000, () => {
console.log("Aplicacao em execucao");
});
Executando e testando o endpoint /status
:
# Executando a aplicação
node index.js
# Testando o GET /status:
curl http://localhost:3000/status
# Resultado:
{"status":"OK"}
Autenticando o usuário e criando o JWT
Vamos criar uma nova rota, POST /login
, para validar as credenciais de um usuário e gerar um JWT assinado com nossa chavePrivada
:
// index.js
// ... (código ocultado) ...
app.post(
"/login",
(req, res) => {
// ************************************
// Observação: o comando abaixo
// const { usuario, senha } = req.body;
// é a mesma coisa que digitar:
// const usuario = req.body.usuario;
// const senha = req.body.senha;
// ************************************
const { usuario, senha } = req.body;
if (usuario === "marcelo" && senha === "123456") {
const jwt = require("jsonwebtoken");
const dadosUsuario = {
nome: "marcelo",
email: "[email protected]",
id: 1
};
const chavePrivada = "consolelog.com.br";
jwt.sign(dadosUsuario, chavePrivada, (err, token) => {
if (err) {
res
.status(500)
.json({ mensagem: "Erro ao gerar o JWT" });
return;
}
res.set("x-access-token", token);
res.end();
});
} else {
res.status(401);
res.end();
}
}
);
// ... (código ocultado) ...
Executando os testes:
# Usuário e senha corretos:
curl -i
-X POST http://localhost:3000/login
--header "content-type: application/json"
-d '{ "usuario": "marcelo", "senha": "123456" }'
HTTP/1.1 200 OK
X-Powered-By: Express
x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub21lIjoibWFyY2VsbyIsImVtYWlsIjoidGVzdGVAZ21haWwuY29tIiwiaWQiOjEsImlhdCI6MTYxNDY4ODg0NH0._FjjvgFFNPLYMp2Oi_1nK5yaRI3zslKag83SLgxx7_U
# Senha inválida:
curl -i
-X POST http://localhost:3000/login
--header "content-type: application/json"
-d '{ "usuario": "marcelo", "senha": "123457" }'
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Validando o JWT (Json Web Token)
Agora vamos criar um outro endpoint, GET /user
, para obter as informações do usuário logado:
// index.js
// ... (código ocultado) ...
app.get(
"/user",
(req, res) => {
const jwt = req.headers["authorization"];
const chavePrivada = "consolelog.com.br";
// Efetuando a validação do JWT:
const jwtService = require("jsonwebtoken");
jwtService.verify(jwt, chavePrivada, (err, userInfo) => {
if (err) {
res.status(403).end();
return;
}
res.json(userInfo);
});
}
);
// ... (código ocultado) ...
Resultado:
# JWT válido gerado na etapa anterior:
curl -i http://localhost:3000/user
--header "authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub21lIjoibWFyY2VsbyIsImVtYWlsIjoidGVzdGVAZ21haWwuY29tIiwiaWQiOjEsImlhdCI6MTYxNDY4ODg0NH0._FjjvgFFNPLYMp2Oi_1nK5yaRI3zslKag83SLgxx7_U"
# Resultado
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 68
{"nome":"marcelo","email":"[email protected]","id":1,"iat":1614688844}
# JWT inválido
curl -i http://localhost:3000/user
--header "authorization: 123"
# Resultado
HTTP/1.1 403 Forbidden
X-Powered-By: Express
Utilizando um middleware
Repare que temos os seguintes endpoints:
GET /status
- não precisa de autenticaçãoPOST /login
- efetua a autenticação de um usuário gerando um JWTGET /user
- retorna as informações do usuário caso o JWT seja válido
Conforme sua aplicação cresce vamos ter novas rotas do tipo:
POST /perfis
- cadastra um perfil no sistemaGET /fotos
- retorna as fotos do usuário
Cada nova rota que exigir a identificação do usuário será necessário validar o JWT e depois obter as informações como o id
do usuário por exemplo. Para não repetir a lógica criada na rota GET /user
, vamos criar um middleware e vinculá-lo apenas às rotas que desejarmos. Para fazer esta alteração devemos alterar o arquivo index.js
para criar o middleware:
// index.js
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const middlewareValidarJWT = (req, res, next) => {
const jwt = req.headers["authorization"];
const chavePrivada = "consolelog.com.br";
// Efetuando a validação do JWT:
const jwtService = require("jsonwebtoken");
jwtService.verify(jwt, chavePrivada, (err, userInfo) => {
if (err) {
res.status(403).end();
return;
}
// O objeto "req" é alterado abaixo
// recebendo uma nova propriedade userInfo.
// Este mesmo objeto chegará na rota
// podendo acessar o req.userInfo
req.userInfo = userInfo;
next();
});
};
// ... (código ocultado) ...
app.get(
"/user",
middlewareValidarJWT, // <<< INCLUSÃO DO MIDDLEWARE
(req, res) => {
res.json(req.userInfo);
}
);
// ... (código ocultado) ...
A alteração acima não mudou o funcionamento do código. Essa refatoração permitiu isolar a lógica de validação do JWT em um middleware que agora pode ser reaproveitada em outras rotas. Para testar este reaproveitamento, vamos criar uma nova rota conforme o código abaixo:
// ... (código ocultado) ...
app.get(
"/fotos",
middlewareValidarJWT,
(req, res) => {
const { id } = req.userInfo;
res.json({ dados: `Fotos do usuário de id: ${id}` });
}
);
// ... (código ocultado) ...
Ao executar a aplicação e testar este novo endpoint, GET /fotos
, obtemos o seguinte resultado:
# Requisição com JWT válido
curl -i http://localhost:3000/fotos
--header "authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub21lIjoibWFyY2VsbyIsImVtYWlsIjoidGVzdGVAZ21haWwuY29tIiwiaWQiOjEsImlhdCI6MTYxNDY4ODg0NH0._FjjvgFFNPLYMp2Oi_1nK5yaRI3zslKag83SLgxx7_U"
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
{"dados":"Fotos do usuário de id: 1"}
# Com um JWT inválido:
curl -i http://localhost:3000/fotos
--header "authorization: 123"
HTTP/1.1 403 Forbidden
X-Powered-By: Express
Código completo:
const express = require('express');
const app = express();
const middlewareValidarJWT = (req, res, next) => {
const jwt = req.headers["authorization"];
const chavePrivada = "consolelog.com.br";
// Efetuando a validação do JWT:
const jwtService = require("jsonwebtoken");
jwtService.verify(jwt, chavePrivada, (err, userInfo) => {
if (err) {
res.status(403).end();
return;
}
req.userInfo = userInfo;
next();
});
};
// Efetua o parse do application/json
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get(
"/status",
(req, res) => {
const jwt = req.headers['authorization'];
res.json({
status: "OK",
jwt
});
}
);
app.post(
"/login",
(req, res) => {
const { usuario, senha } = req.body;
if (usuario === "marcelo" && senha === "123456") {
const jwt = require("jsonwebtoken");
const dadosUsuario = {
nome: "marcelo",
email: "[email protected]",
id: 1
};
const chavePrivada = "consolelog.com.br";
jwt.sign(dadosUsuario, chavePrivada, (err, token) => {
if (err) {
res
.status(500)
.json({ mensagem: "Erro ao gerar o JWT" });
return;
}
res.set("x-access-token", token);
res.end();
});
} else {
res.status(401);
res.end();
}
}
);
app.get(
"/user",
middlewareValidarJWT,
(req, res) => {
res.json(req.userInfo);
}
);
app.get(
"/fotos",
middlewareValidarJWT,
(req, res) => {
const { id } = req.userInfo;
res.json({ dados: `Fotos do usuário de id: ${id}` });
}
);
app.listen(3000, () => {
console.log("Aplicacao em execucao");
});
Links interessantes: