From 21167ccda45a4d5bec713fef362078c946287e93 Mon Sep 17 00:00:00 2001 From: Thais Kotovicz Date: Mon, 24 Apr 2023 18:17:18 -0300 Subject: [PATCH 1/5] feat: cria middleware para salvar token nos cookies --- database.sqlite | Bin 20480 -> 20480 bytes package-lock.json | 31 ++++++++++++++++++++ package.json | 2 ++ src/interfaces/IRequestWithUser.ts | 6 ++++ src/middlewares/authCookieMiddleware.ts | 17 +++++++++++ src/middlewares/authMiddleware.ts | 37 +++++++++++++++++------- src/routes/Exchange.ts | 2 ++ src/services/jwtService.ts | 2 +- 8 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/interfaces/IRequestWithUser.ts create mode 100644 src/middlewares/authCookieMiddleware.ts diff --git a/database.sqlite b/database.sqlite index f3452251ead4bbb60155b26a33b188ec3fb27b8f..6c1652a71f553dae779483052e7496690b04eb1d 100644 GIT binary patch delta 137 zcmZozz}T>Wae_1>%S0JxMwX2UOZYjM_?I*AU*%uESx{g$Kc5a03xlk0X>n>%A`nb| zAg?g_s=Pi>(JKc2@BFVe3o6{>SKwimWrQhV;$Rl%EJ-acN#$i=U|{6`%E12>sOlO2 a!~j-7PDW;N#!#SP#Y`Ys1_6l71Q7t6bS2mT delta 198 zcmZozz}T>Wae_1>>qHr6M%Il9OZeFs`L8nYU)?OIaGHPeRe60N|2qT!_sxP1Z}~+z zn8i6uQj1Gcc^McOnE0PF@PFffzFE-VHoqz_6SF*{YejNKVqSV`F%t(PP-$p!YEdyn z=>`V=8~hu9rY_+RP-Eg`U=-#QW@l$*at#d%4R*2r@HIHoc!2|qW^8*g?YOBLBZ@kw WARh;@?1!(Nm~O#npt`B=zXJfObUTFr diff --git a/package-lock.json b/package-lock.json index ff98745..8798991 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", @@ -25,6 +26,7 @@ "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/chai-http": "^4.2.0", + "@types/cookie-parser": "^1.4.3", "@types/cors": "^2.8.13", "@types/express": "^4.17.17", "@types/joi": "^17.2.3", @@ -577,6 +579,15 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", + "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", @@ -1626,6 +1637,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/package.json b/package.json index 7d2dd10..99fa227 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "homepage": "https://bitbucket.org/recrutamento_jya_nodejs/recrutamento-conversor-nodejs-thais_kotovicz_hotmail.com#readme", "dependencies": { + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", @@ -39,6 +40,7 @@ "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/chai-http": "^4.2.0", + "@types/cookie-parser": "^1.4.3", "@types/cors": "^2.8.13", "@types/express": "^4.17.17", "@types/joi": "^17.2.3", diff --git a/src/interfaces/IRequestWithUser.ts b/src/interfaces/IRequestWithUser.ts new file mode 100644 index 0000000..935f72d --- /dev/null +++ b/src/interfaces/IRequestWithUser.ts @@ -0,0 +1,6 @@ +import { Request } from 'express'; +import { IUserJwt } from '../services/jwtService'; + +export default interface IRequestWithUser extends Request { + user?: IUserJwt; +} \ No newline at end of file diff --git a/src/middlewares/authCookieMiddleware.ts b/src/middlewares/authCookieMiddleware.ts new file mode 100644 index 0000000..1b72599 --- /dev/null +++ b/src/middlewares/authCookieMiddleware.ts @@ -0,0 +1,17 @@ +import { Response, NextFunction } from 'express'; +import JwtService, { IUserJwt } from '../services/jwtService'; +import IRequestWithUser from '../interfaces/IRequestWithUser'; + +export default function authCookieMiddleware(req: IRequestWithUser, res: Response, next: NextFunction) { + const userData = req.user; + const token = JwtService.createToken(userData!); + + res.cookie('token', token, { + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 dias em milissegundos + httpOnly: true, + secure: true, + sameSite: 'strict' + }); + + next(); +} \ No newline at end of file diff --git a/src/middlewares/authMiddleware.ts b/src/middlewares/authMiddleware.ts index e63f434..92f3f8d 100644 --- a/src/middlewares/authMiddleware.ts +++ b/src/middlewares/authMiddleware.ts @@ -1,21 +1,38 @@ -import { Request, Response, NextFunction } from 'express'; +import { Response, NextFunction } from 'express'; import { ErrorTypes } from '../errors/catalog'; +import IRequestWithUser from '../interfaces/IRequestWithUser'; import JwtService from '../services/jwtService'; -export default function authMiddleware(req: Request, res: Response, next: NextFunction) { - const authorization = req.headers.authorization; +export default function authMiddleware(req: IRequestWithUser, res: Response, next: NextFunction) { + // salva o token no req.headers (para usar o token de maneira manual) + // const authorization = req.headers.authorization; - if (!authorization) { + // if (!authorization) { + // throw new Error(ErrorTypes.InvalidToken); + // } + + // // o token de autorização é enviado no formato 'Bearer ' + // // o split separa a string em dois e o primeiro elemento `Bearer` não será utilizado + // // A desestruturação permite que o segundo elemento `token` seja atribuído diretamente à variável token + // const [token] = authorization.split(' '); + + // try { + // JwtService.validateToken(token); + // return next(); + // } catch (error) { + // throw new Error(ErrorTypes.InvalidToken); + // } + + // salva o token nos cookies + const tokenCookie = req.cookies.token; + + if (!tokenCookie) { throw new Error(ErrorTypes.InvalidToken); } - // o token de autorização é enviado no formato 'Bearer ' - // o split separa a string em dois e o primeiro elemento `Bearer` não será utilizado - // A desestruturação permite que o segundo elemento `token` seja atribuído diretamente à variável token - const [token] = authorization.split(' '); - try { - JwtService.validateToken(token); + const userData = JwtService.validateToken(tokenCookie); + req.user = userData; return next(); } catch (error) { throw new Error(ErrorTypes.InvalidToken); diff --git a/src/routes/Exchange.ts b/src/routes/Exchange.ts index 79d91ac..2fef8b5 100644 --- a/src/routes/Exchange.ts +++ b/src/routes/Exchange.ts @@ -1,9 +1,11 @@ import { Router } from 'express'; import authMiddleware from '../middlewares/authMiddleware'; import { exchangeController } from './main'; +import authCookieMiddleware from '../middlewares/authCookieMiddleware'; const exchangeRouter = Router(); +exchangeRouter.use(authCookieMiddleware); exchangeRouter.use(authMiddleware); exchangeRouter.post('/exchange', exchangeController.createTransaction); diff --git a/src/services/jwtService.ts b/src/services/jwtService.ts index 5a9472d..f1bb212 100644 --- a/src/services/jwtService.ts +++ b/src/services/jwtService.ts @@ -2,7 +2,7 @@ import 'dotenv/config'; import * as jwt from 'jsonwebtoken'; import { ErrorTypes } from '../errors/catalog'; -interface IUserJwt { +export interface IUserJwt { id: number username: string } From cc1431d8197cc03b44acfee616871b6a49ea83c4 Mon Sep 17 00:00:00 2001 From: Thais Kotovicz Date: Mon, 24 Apr 2023 19:19:24 -0300 Subject: [PATCH 2/5] feat: add a lib cookie-parser no app --- database.sqlite | Bin 20480 -> 20480 bytes src/app.ts | 3 +++ src/middlewares/authCookieMiddleware.ts | 8 ++++---- src/middlewares/authMiddleware.ts | 25 ++++-------------------- 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/database.sqlite b/database.sqlite index 6c1652a71f553dae779483052e7496690b04eb1d..17f6099baafb5ba62a54748072d044d41241b75f 100644 GIT binary patch delta 92 zcmZozz}T>Wae_1>>qHr6M%Il9OZd5%`D+;XuktVFuh}dpkjKxh$;`qa?wgpBo0&KH tfxN=xWae_1>%S0JxMwX2UOZYjM_?I*AU*%uESx{j%|K#QJu|Sbm4E*2uUu_mN RxW~WwmA' - // // o split separa a string em dois e o primeiro elemento `Bearer` não será utilizado - // // A desestruturação permite que o segundo elemento `token` seja atribuído diretamente à variável token - // const [token] = authorization.split(' '); - - // try { - // JwtService.validateToken(token); - // return next(); - // } catch (error) { - // throw new Error(ErrorTypes.InvalidToken); - // } - +export default function authMiddleware(req: IRequestWithUser, res: Response, next: NextFunction) { // salva o token nos cookies - const tokenCookie = req.cookies.token; + const tokenCookie = req.cookies; if (!tokenCookie) { throw new Error(ErrorTypes.InvalidToken); @@ -38,3 +19,5 @@ export default function authMiddleware(req: IRequestWithUser, res: Response, nex throw new Error(ErrorTypes.InvalidToken); } } + +// https://github.com/expressjs/cookie-parser From 759d985f801c93ca998c8d21269d9d3314e216ae Mon Sep 17 00:00:00 2001 From: Thais Kotovicz Date: Mon, 24 Apr 2023 21:38:41 -0300 Subject: [PATCH 3/5] refactor: altera o auth middleware --- src/interfaces/IRequestWithUser.ts | 6 ----- src/middlewares/authMiddleware.ts | 39 ++++++++++++++++++------------ 2 files changed, 24 insertions(+), 21 deletions(-) delete mode 100644 src/interfaces/IRequestWithUser.ts diff --git a/src/interfaces/IRequestWithUser.ts b/src/interfaces/IRequestWithUser.ts deleted file mode 100644 index 935f72d..0000000 --- a/src/interfaces/IRequestWithUser.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Request } from 'express'; -import { IUserJwt } from '../services/jwtService'; - -export default interface IRequestWithUser extends Request { - user?: IUserJwt; -} \ No newline at end of file diff --git a/src/middlewares/authMiddleware.ts b/src/middlewares/authMiddleware.ts index fc071e9..fe2e14f 100644 --- a/src/middlewares/authMiddleware.ts +++ b/src/middlewares/authMiddleware.ts @@ -1,23 +1,32 @@ -import { Response, NextFunction } from 'express'; +import 'dotenv/config'; +import { Request, Response, NextFunction } from 'express'; +import jwt from 'jsonwebtoken'; import { ErrorTypes } from '../errors/catalog'; -import IRequestWithUser from '../interfaces/IRequestWithUser'; -import JwtService from '../services/jwtService'; -export default function authMiddleware(req: IRequestWithUser, res: Response, next: NextFunction) { - // salva o token nos cookies - const tokenCookie = req.cookies; +interface AuthRequest extends Request { + userId: string; +} - if (!tokenCookie) { - throw new Error(ErrorTypes.InvalidToken); - } +interface DecodedToken { + id: string; + // outras propriedades do payload do token, se houver +} + +const secret = process.env.JWT_SECRET as string; - try { - const userData = JwtService.validateToken(tokenCookie); - req.user = userData; - return next(); - } catch (error) { +export default function authMiddleware(req: AuthRequest, res: Response, next: NextFunction) { + const token = req.cookies?.token ?? null; + + if (!token) { throw new Error(ErrorTypes.InvalidToken); } -} + + jwt.verify(token, secret, (err: jwt.JsonWebTokenError, decoded: DecodedToken) => { + if (err) throw new Error(ErrorTypes.InvalidToken); + + req.userId = decoded.id; + next(); + }); +} // https://github.com/expressjs/cookie-parser From dbb35e7e42fe5edbd6ed385c7e6f195ff7e9a090 Mon Sep 17 00:00:00 2001 From: Thais Kotovicz Date: Mon, 24 Apr 2023 21:40:47 -0300 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20remove=20arquivo=20que=20n?= =?UTF-8?q?=C3=A3o=20ser=C3=A1=20mais=20usado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/middlewares/authCookieMiddleware.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/middlewares/authCookieMiddleware.ts diff --git a/src/middlewares/authCookieMiddleware.ts b/src/middlewares/authCookieMiddleware.ts deleted file mode 100644 index b5de00b..0000000 --- a/src/middlewares/authCookieMiddleware.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Response, NextFunction } from 'express'; -import JwtService from '../services/jwtService'; -import IRequestWithUser from '../interfaces/IRequestWithUser'; - -export default function authCookieMiddleware(req: IRequestWithUser, res: Response, next: NextFunction) { - const userData = req.user; - const token = JwtService.createToken(userData!); - - res.cookie('token', token, { - maxAge: 7 * 24 * 60 * 60 * 1000, // 7 dias em milissegundos - httpOnly: true, - // secure: true, - // sameSite: 'strict' - }); - - next(); -} From 3ddac1050bf0acb506f4c143b6b9d0ee794acda7 Mon Sep 17 00:00:00 2001 From: Thais Kotovicz <93948349+trkotovicz@users.noreply.github.com> Date: Mon, 24 Apr 2023 21:46:39 -0300 Subject: [PATCH 5/5] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 903cbc1..fd36bd6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A API registra cada transação de conversão com todas as informações relacio A aplicação foi desenvolvida no modelo de camadas MSC (Model-Service-Conntroller) em Node.js, utilizando Typescript, Express.js, banco de dados SQLite, ORM Sequelize e Swagger para a documentação. Tem autenticação de usuário dom Json Web Token (JWT). Além disso, é possível radar a aplicação pelo Docker.
Para os testes unitários e de integração, foi utilizado Mocha, Chai e Sinon.
-Para deploy da aplicação, foi utilizado o [Railway](https://exchange-api.up.railway.app/). +Para deploy da aplicação, foi utilizado o [Render](https://exchange-api.onrender.com). ## Inicialização da Aplicação @@ -45,8 +45,7 @@ Com a aplicação rodando acesse a [documentação da API](http://localhost:3001 ## Melhorias Futuras -- Corrigir o erro de deploy no Railway (está retornando um erro de token inválido ao tentar acessar a aplicação). -- Adicionar um fluxo de entrega contínua (CI/CD). +- Corrigir o erro de deploy no Render (está retornando um erro de token inválido ao tentar acessar a aplicação). - Melhorar os testes unitários e de integração da aplicação. ## Itens desejáveis @@ -61,7 +60,7 @@ Com a aplicação rodando acesse a [documentação da API](http://localhost:3001 - [x] Testes de integração - [x] Documentação dos endpoints - [x] Estar rodando e disponível (Ex: Heroku, ou similar) - - [ ] CI/CD + - [x] CI/CD ---