Skip to content

Commit 6db2f3c

Browse files
committed
feat: login ,register and protect route
1 parent d1a956a commit 6db2f3c

File tree

8 files changed

+143
-4
lines changed

8 files changed

+143
-4
lines changed

.env

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22

33
PORT = 5050
44
MONGOURI = <Your-MongoDB-URI>
5+
6+
JWT_SECRET ="itsyourjwtsecrentusesomethingbigtoprotectyourjwtauthentication"
7+
JWT_COOKIE_EXPIRES_IN = 7
8+

package-lock.json

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"jsonwebtoken": "^9.0.2",
3636
"mongoose": "^8.0.2",
3737
"morgan": "^1.10.0",
38+
"validator": "^13.11.0",
3839
"xss-clean": "^0.1.4"
3940
},
4041
"devDependencies": {

src/config/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ dotenv.config();
44
const config = {
55
PORT: process.env.PORT || 3000,
66
MONGOURI: process.env.MONGOURI,
7+
JWT_SECRET: process.env.JWT_SECRET,
8+
JWT_COOKIE_EXPIRES_IN: process.env.JWT_COOKIE_EXPIRES_IN,
79
};
810

911
export default config;

src/controllers/authController.ts

+82
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,88 @@
11
import { Request, Response } from 'express';
22
import asyncHandler from '../util/catchAsync';
3+
import UserModel from '../models/userSchema';
4+
import jwt from 'jsonwebtoken';
5+
import bcrypt from 'bcryptjs';
6+
import validator from 'validator';
7+
import { CookieOptions } from '../interfaces/cookieOption';
8+
import config from '../config/config';
39

410
export const testRoute = asyncHandler(async (req: Request, res: Response) => {
511
res.json({ success: true });
612
});
13+
14+
export const login = asyncHandler(async (req: Request, res: Response) => {
15+
try {
16+
const { email, password } = req.body;
17+
const user = await UserModel.findOne({ email });
18+
if (!user) {
19+
return res.status(404).json({
20+
success: false,
21+
message: 'Invalid credentials',
22+
});
23+
}
24+
const isPasswordMatch = await bcrypt.compare(password, user.password);
25+
if (!isPasswordMatch) {
26+
return res.status(401).json({
27+
success: false,
28+
message: 'Invalid credentials',
29+
});
30+
}
31+
const token = jwt.sign({ userId: user._id }, config.JWT_SECRET);
32+
const expireTime: number = parseInt(config.JWT_COOKIE_EXPIRES_IN);
33+
34+
const cookieOptions: CookieOptions = {
35+
expires: new Date(Date.now() + expireTime * 24 * 60 * 60 * 1000),
36+
httpOnly: true,
37+
secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
38+
sameSite: 'strict',
39+
};
40+
res.cookie('jwt', token, cookieOptions);
41+
user.password = undefined;
42+
user.cpassword = undefined;
43+
res.setHeader('Authorization', `Bearer ${token}`);
44+
45+
res.status(200).json({ success: true, data: user, jwt_token: token });
46+
} catch (error) {
47+
console.error('Login error:', error);
48+
res.status(500).json({
49+
success: false,
50+
message: 'Internal server error',
51+
});
52+
}
53+
});
54+
55+
export const register = asyncHandler(async (req: Request, res: Response) => {
56+
const { name, email, password, cpassword } = req.body;
57+
58+
if (!name || !email || !password || !cpassword || !validator.isEmail(email)) {
59+
return res
60+
.status(400)
61+
.json({ message: 'Invalid input data!', success: false });
62+
}
63+
const checkUser = await UserModel.findOne({ email });
64+
if (checkUser) {
65+
return res
66+
.status(409)
67+
.json({ success: false, message: 'User already exists!' });
68+
}
69+
try {
70+
const user = new UserModel({
71+
name,
72+
email,
73+
password,
74+
cpassword,
75+
});
76+
await user.save();
77+
return res.status(201).json({
78+
message: 'Registration successful!',
79+
success: true,
80+
userId: user._id,
81+
});
82+
} catch (error) {
83+
console.error('Registration error:', error);
84+
return res
85+
.status(500)
86+
.json({ message: 'Registration failed!', success: false });
87+
}
88+
});

src/interfaces/cookieOption.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface CookieOptions {
2+
expires: Date;
3+
httpOnly: boolean;
4+
secure: boolean;
5+
sameSite: boolean | 'strict' | 'lax' | 'none';
6+
}

src/middleware/middleware.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Request as ExpressRequest, Response, NextFunction } from 'express';
2+
import jwt from 'jsonwebtoken';
3+
import config from '../config/config';
4+
5+
interface RequestWithUserId extends ExpressRequest {
6+
userId?: string;
7+
}
8+
9+
export const protect = (
10+
req: RequestWithUserId,
11+
res: Response,
12+
next: NextFunction
13+
) => {
14+
try {
15+
const token = req.headers.authorization?.split(' ')[1];
16+
if (!token) {
17+
return res.status(401).json({
18+
success: false,
19+
message: 'Unauthorized: No token provided',
20+
});
21+
}
22+
23+
const decoded = jwt.verify(token, config.JWT_SECRET) as { userId: string };
24+
req.userId = decoded.userId;
25+
next();
26+
} catch (error) {
27+
console.error('Authentication error:', error);
28+
return res.status(401).json({
29+
success: false,
30+
message: 'Unauthorized: Invalid token',
31+
});
32+
}
33+
};

src/routes/authRoutes.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import express, { Router } from 'express';
2-
import { testRoute } from '../controllers/authController';
3-
const route: Router = express.Router();
2+
import { testRoute, login, register } from '../controllers/authController';
3+
const router: Router = express.Router();
44

5-
route.get('/', testRoute);
5+
router.get('/', testRoute);
6+
router.post('/login', login);
7+
router.post('/register', register);
68

7-
export default route;
9+
export default router;

0 commit comments

Comments
 (0)