From c2e4aacb28423f93e4c03626783c6f42b97ebb80 Mon Sep 17 00:00:00 2001 From: Guillaume Dorce Date: Fri, 26 Aug 2022 15:00:24 +0200 Subject: [PATCH] save token in db when generated --- package.json | 2 ++ pnpm-lock.yaml | 8 ++++++++ prisma/schema.prisma | 16 +++++++++++++--- src/api/auth/login.ts | 4 ++-- src/api/posts/index.ts | 5 ++--- src/controller/AuthController.ts | 21 ++++++++++++++++----- src/models/TokenModel.ts | 18 ++++++++++++++++-- 7 files changed, 59 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index a2d55f6..169231b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@types/cors": "^2.8.12", "@types/express": "^4.17.13", "@types/jsonwebtoken": "^8.5.8", + "@types/ms": "^0.7.31", "@types/node": "^18.7.4", "nodemon": "^2.0.19", "prisma": "^4.2.1", @@ -32,6 +33,7 @@ "dotenv": "^16.0.1", "express": "^4.18.1", "jsonwebtoken": "^8.5.1", + "ms": "^2.1.3", "zod": "^3.18.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f77206e..36a7e6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,12 +6,14 @@ specifiers: '@types/cors': ^2.8.12 '@types/express': ^4.17.13 '@types/jsonwebtoken': ^8.5.8 + '@types/ms': ^0.7.31 '@types/node': ^18.7.4 bcrypt: ^5.0.1 cors: ^2.8.5 dotenv: ^16.0.1 express: ^4.18.1 jsonwebtoken: ^8.5.1 + ms: ^2.1.3 nodemon: ^2.0.19 prisma: ^4.2.1 ts-node: ^10.9.1 @@ -26,6 +28,7 @@ dependencies: dotenv: 16.0.1 express: 4.18.1 jsonwebtoken: 8.5.1 + ms: 2.1.3 zod: 3.18.0 devDependencies: @@ -33,6 +36,7 @@ devDependencies: '@types/cors': 2.8.12 '@types/express': 4.17.13 '@types/jsonwebtoken': 8.5.8 + '@types/ms': 0.7.31 '@types/node': 18.7.4 nodemon: 2.0.19 prisma: 4.2.1 @@ -171,6 +175,10 @@ packages: resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} dev: true + /@types/ms/0.7.31: + resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + dev: true + /@types/node/18.7.4: resolution: {integrity: sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==} dev: true diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6332c51..ed33b53 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -10,14 +10,15 @@ datasource db { } model User { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) firstName String lastName String - email String @unique + email String @unique password String posts Post[] - role Role @default(USER) + role Role @default(USER) likes Like[] + tokens Token[] } enum Role { @@ -44,3 +45,12 @@ model Like { postId Int post Post @relation(fields: [postId], references: [id], onDelete: Cascade) } + +model Token { + id Int @id @default(autoincrement()) + token String @unique + userId Int + user User @relation(fields: [userId], references: [id]) + createdAt DateTime @default(now()) + expiresAt DateTime @default(now()) +} diff --git a/src/api/auth/login.ts b/src/api/auth/login.ts index f941112..55949e7 100644 --- a/src/api/auth/login.ts +++ b/src/api/auth/login.ts @@ -15,8 +15,8 @@ const login = async (req: Request, res: Response) => { if (!isValid) { return res.status(401).send({ error: 'Invalid password' }); } - const token = genToken(user.id); - return res.status(200).send({ token, userId: user.id }); + const token = await genToken(user.id); + return res.status(200).send({ token: token.token, userId: user.id, expiresAt: token.expiresAt }); } catch (error) { return res.status(500).send(error); } diff --git a/src/api/posts/index.ts b/src/api/posts/index.ts index 01d17dc..0d3d424 100644 --- a/src/api/posts/index.ts +++ b/src/api/posts/index.ts @@ -6,7 +6,6 @@ import deletePost from './deletePost'; import likePost from './likePost'; import unlikePost from './unlikePost'; import { verifyToken } from '@/controller/AuthController'; -import { Token } from '@/models/TokenModel'; const posts = Router(); @@ -21,8 +20,8 @@ const checkAuth = (req: Request, res: Response, next: NextFunction) => { return res.status(401).send('No token provided'); } return verifyToken(token) - .then((decodedToken: Token) => { - req.userId = decodedToken.id; + .then((decodedToken: number) => { + req.userId = decodedToken; next(); }) .catch(() => { diff --git a/src/controller/AuthController.ts b/src/controller/AuthController.ts index 536adf3..a0f2ce1 100644 --- a/src/controller/AuthController.ts +++ b/src/controller/AuthController.ts @@ -2,7 +2,10 @@ import * as jwt from 'jsonwebtoken'; import * as bcrypt from 'bcrypt'; import { promisify } from 'util'; import { config } from '..'; -import { Token } from '@/models/TokenModel'; +import { PrismaClient } from '@prisma/client'; +import ms from 'ms'; + +const prisma = new PrismaClient(); const saltRounds = 10; const hashPromise = promisify(bcrypt.hash); @@ -15,13 +18,21 @@ const comparePassword = (password: string, hash: string) => { return bcrypt.compare(password, hash); }; -const genToken = (id: number) => { - return jwt.sign({ id }, config.JWT_SECRET, { +const genToken = (userId: number) => { + const newToken = jwt.sign({ id: userId }, config.JWT_SECRET, { expiresIn: config.JWT_EXPIRES_IN, }); + + return prisma.token.create({ + data: { + userId, + token: newToken, + expiresAt: new Date(Date.now() + ms(config.JWT_EXPIRES_IN)), // 30 days + }, + }); }; -const verifyToken = (token: string): Promise => { +const verifyToken = (token: string): Promise => { return new Promise((resolve, reject) => { jwt.verify(token, config.JWT_SECRET, (err?, decoded?: jwt.JwtPayload | string) => { if (err) { @@ -29,7 +40,7 @@ const verifyToken = (token: string): Promise => { } else if (decoded === undefined || typeof decoded === 'string' || decoded.id === undefined) { reject('Invalid token'); } else { - const decodedToken: Token = { id: decoded.id }; + const decodedToken: number = decoded.id; resolve(decodedToken); } }); diff --git a/src/models/TokenModel.ts b/src/models/TokenModel.ts index 598f6d5..c01bfcf 100644 --- a/src/models/TokenModel.ts +++ b/src/models/TokenModel.ts @@ -1,5 +1,19 @@ -type Token = { +import { z } from 'zod'; + +interface Token { id: number; -}; + userId: number; + token: string; + createdAt: Date; + updatedAt: Date; +} + +const Token: z.ZodType = z.object({ + id: z.number(), + userId: z.number(), + token: z.string(), + createdAt: z.date(), + updatedAt: z.date(), +}); export { Token };