save token in db when generated
This commit is contained in:
parent
3261158588
commit
c2e4aacb28
|
|
@ -18,6 +18,7 @@
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/jsonwebtoken": "^8.5.8",
|
"@types/jsonwebtoken": "^8.5.8",
|
||||||
|
"@types/ms": "^0.7.31",
|
||||||
"@types/node": "^18.7.4",
|
"@types/node": "^18.7.4",
|
||||||
"nodemon": "^2.0.19",
|
"nodemon": "^2.0.19",
|
||||||
"prisma": "^4.2.1",
|
"prisma": "^4.2.1",
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
"ms": "^2.1.3",
|
||||||
"zod": "^3.18.0"
|
"zod": "^3.18.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ specifiers:
|
||||||
'@types/cors': ^2.8.12
|
'@types/cors': ^2.8.12
|
||||||
'@types/express': ^4.17.13
|
'@types/express': ^4.17.13
|
||||||
'@types/jsonwebtoken': ^8.5.8
|
'@types/jsonwebtoken': ^8.5.8
|
||||||
|
'@types/ms': ^0.7.31
|
||||||
'@types/node': ^18.7.4
|
'@types/node': ^18.7.4
|
||||||
bcrypt: ^5.0.1
|
bcrypt: ^5.0.1
|
||||||
cors: ^2.8.5
|
cors: ^2.8.5
|
||||||
dotenv: ^16.0.1
|
dotenv: ^16.0.1
|
||||||
express: ^4.18.1
|
express: ^4.18.1
|
||||||
jsonwebtoken: ^8.5.1
|
jsonwebtoken: ^8.5.1
|
||||||
|
ms: ^2.1.3
|
||||||
nodemon: ^2.0.19
|
nodemon: ^2.0.19
|
||||||
prisma: ^4.2.1
|
prisma: ^4.2.1
|
||||||
ts-node: ^10.9.1
|
ts-node: ^10.9.1
|
||||||
|
|
@ -26,6 +28,7 @@ dependencies:
|
||||||
dotenv: 16.0.1
|
dotenv: 16.0.1
|
||||||
express: 4.18.1
|
express: 4.18.1
|
||||||
jsonwebtoken: 8.5.1
|
jsonwebtoken: 8.5.1
|
||||||
|
ms: 2.1.3
|
||||||
zod: 3.18.0
|
zod: 3.18.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
|
@ -33,6 +36,7 @@ devDependencies:
|
||||||
'@types/cors': 2.8.12
|
'@types/cors': 2.8.12
|
||||||
'@types/express': 4.17.13
|
'@types/express': 4.17.13
|
||||||
'@types/jsonwebtoken': 8.5.8
|
'@types/jsonwebtoken': 8.5.8
|
||||||
|
'@types/ms': 0.7.31
|
||||||
'@types/node': 18.7.4
|
'@types/node': 18.7.4
|
||||||
nodemon: 2.0.19
|
nodemon: 2.0.19
|
||||||
prisma: 4.2.1
|
prisma: 4.2.1
|
||||||
|
|
@ -171,6 +175,10 @@ packages:
|
||||||
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
|
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/ms/0.7.31:
|
||||||
|
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node/18.7.4:
|
/@types/node/18.7.4:
|
||||||
resolution: {integrity: sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==}
|
resolution: {integrity: sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,15 @@ datasource db {
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
firstName String
|
firstName String
|
||||||
lastName String
|
lastName String
|
||||||
email String @unique
|
email String @unique
|
||||||
password String
|
password String
|
||||||
posts Post[]
|
posts Post[]
|
||||||
role Role @default(USER)
|
role Role @default(USER)
|
||||||
likes Like[]
|
likes Like[]
|
||||||
|
tokens Token[]
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
|
|
@ -44,3 +45,12 @@ model Like {
|
||||||
postId Int
|
postId Int
|
||||||
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
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())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ const login = async (req: Request, res: Response) => {
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return res.status(401).send({ error: 'Invalid password' });
|
return res.status(401).send({ error: 'Invalid password' });
|
||||||
}
|
}
|
||||||
const token = genToken(user.id);
|
const token = await genToken(user.id);
|
||||||
return res.status(200).send({ token, userId: user.id });
|
return res.status(200).send({ token: token.token, userId: user.id, expiresAt: token.expiresAt });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(500).send(error);
|
return res.status(500).send(error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import deletePost from './deletePost';
|
||||||
import likePost from './likePost';
|
import likePost from './likePost';
|
||||||
import unlikePost from './unlikePost';
|
import unlikePost from './unlikePost';
|
||||||
import { verifyToken } from '@/controller/AuthController';
|
import { verifyToken } from '@/controller/AuthController';
|
||||||
import { Token } from '@/models/TokenModel';
|
|
||||||
|
|
||||||
const posts = Router();
|
const posts = Router();
|
||||||
|
|
||||||
|
|
@ -21,8 +20,8 @@ const checkAuth = (req: Request, res: Response, next: NextFunction) => {
|
||||||
return res.status(401).send('No token provided');
|
return res.status(401).send('No token provided');
|
||||||
}
|
}
|
||||||
return verifyToken(token)
|
return verifyToken(token)
|
||||||
.then((decodedToken: Token) => {
|
.then((decodedToken: number) => {
|
||||||
req.userId = decodedToken.id;
|
req.userId = decodedToken;
|
||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ import * as jwt from 'jsonwebtoken';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { config } from '..';
|
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 saltRounds = 10;
|
||||||
const hashPromise = promisify(bcrypt.hash);
|
const hashPromise = promisify(bcrypt.hash);
|
||||||
|
|
@ -15,13 +18,21 @@ const comparePassword = (password: string, hash: string) => {
|
||||||
return bcrypt.compare(password, hash);
|
return bcrypt.compare(password, hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
const genToken = (id: number) => {
|
const genToken = (userId: number) => {
|
||||||
return jwt.sign({ id }, config.JWT_SECRET, {
|
const newToken = jwt.sign({ id: userId }, config.JWT_SECRET, {
|
||||||
expiresIn: config.JWT_EXPIRES_IN,
|
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<Token> => {
|
const verifyToken = (token: string): Promise<number> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
jwt.verify(token, config.JWT_SECRET, (err?, decoded?: jwt.JwtPayload | string) => {
|
jwt.verify(token, config.JWT_SECRET, (err?, decoded?: jwt.JwtPayload | string) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -29,7 +40,7 @@ const verifyToken = (token: string): Promise<Token> => {
|
||||||
} else if (decoded === undefined || typeof decoded === 'string' || decoded.id === undefined) {
|
} else if (decoded === undefined || typeof decoded === 'string' || decoded.id === undefined) {
|
||||||
reject('Invalid token');
|
reject('Invalid token');
|
||||||
} else {
|
} else {
|
||||||
const decodedToken: Token = { id: decoded.id };
|
const decodedToken: number = decoded.id;
|
||||||
resolve(decodedToken);
|
resolve(decodedToken);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
type Token = {
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
interface Token {
|
||||||
id: number;
|
id: number;
|
||||||
};
|
userId: number;
|
||||||
|
token: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token: z.ZodType<Token> = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
userId: z.number(),
|
||||||
|
token: z.string(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
});
|
||||||
|
|
||||||
export { Token };
|
export { Token };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue