Skip to content

Commit a4c8fd1

Browse files
committed
Added test cases along with the Authentication endpoints
1 parent 33e8405 commit a4c8fd1

7 files changed

Lines changed: 975 additions & 28 deletions

File tree

app/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
PORT=your_port_here
22
DB_URI=your_db_uri_here
33
JWT_SECRET=your_jwt_secret_here
4+
JWT_REFRESH_SECRET=your_jwt_refresh_secret_here

app/controllers/authController.js

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@ import bcrypt from 'bcrypt';
22
import jwt from 'jsonwebtoken';
33
import User, { ROLES, PERMISSIONS } from '../models/User.js';
44
import winston from 'winston';
5+
import dotenv from 'dotenv';
6+
7+
dotenv.config();
58

69
// === Logger Setup ===
710
const logger = winston.createLogger({
811
level: 'info',
912
format: winston.format.json(),
10-
transports: [
11-
new winston.transports.File({ filename: 'logs/auth.log' }),
12-
],
13+
transports: [new winston.transports.File({ filename: 'logs/auth.log' })],
1314
});
1415

1516
// === Secrets from Environment ===
1617
const JWT_SECRET = process.env.JWT_SECRET;
1718
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET;
1819

20+
if (!JWT_SECRET || !JWT_REFRESH_SECRET) {
21+
console.error("❌ JWT secrets are missing from .env");
22+
}
23+
1924
// === Generate Access + Refresh Tokens ===
2025
const generateTokens = (user) => {
2126
const payload = {
@@ -60,30 +65,41 @@ export const register = async (req, res) => {
6065
});
6166

6267
await newUser.save();
63-
logger.info(`User registered: ${email} by ${addedBy}`);
68+
logger.info(`User registered: ${email} by ${addedBy}`);
6469

6570
res.status(201).json({ message: 'User registered successfully.' });
6671

6772
} catch (err) {
68-
logger.error('Registration failed', err);
73+
logger.error('❌ Registration failed', err);
74+
console.error("REGISTER ERROR:", err);
6975
res.status(500).json({ message: 'Server error during registration.' });
7076
}
7177
};
7278

7379
// === LOGIN ===
7480
export const login = async (req, res) => {
7581
try {
82+
console.log("ENV JWT_SECRET:", !!JWT_SECRET);
83+
console.log("ENV JWT_REFRESH_SECRET:", !!JWT_REFRESH_SECRET);
84+
7685
const { email, password } = req.body;
86+
console.log("Login attempt:", email);
7787

7888
const user = await User.findOne({ email });
7989
if (!user) return res.status(400).json({ message: 'Invalid credentials.' });
8090

8191
const isMatch = await bcrypt.compare(password, user.password);
92+
console.log("Password match:", isMatch);
93+
8294
if (!isMatch) return res.status(400).json({ message: 'Invalid credentials.' });
8395

8496
const { accessToken, refreshToken } = generateTokens(user);
8597

86-
logger.info(`User login: ${email}`);
98+
// Save refresh token for future verification
99+
user.refreshToken = refreshToken;
100+
await user.save();
101+
102+
logger.info(`✅ User login: ${email}`);
87103

88104
res.status(200).json({
89105
accessToken,
@@ -98,7 +114,8 @@ export const login = async (req, res) => {
98114
});
99115

100116
} catch (err) {
101-
logger.error('Login failed', err);
117+
logger.error('❌ Login failed', err);
118+
console.error("LOGIN ERROR:", err);
102119
res.status(500).json({ message: 'Server error during login.' });
103120
}
104121
};
@@ -114,8 +131,7 @@ export const logout = async (req, res) => {
114131
}
115132
};
116133

117-
118-
// 🔁 Issue a new access token using refresh token
134+
// === REFRESH TOKEN ===
119135
export const refreshToken = async (req, res) => {
120136
const token = req.body.token;
121137
if (!token) return res.status(401).json({ message: 'Refresh token required' });
@@ -124,49 +140,46 @@ export const refreshToken = async (req, res) => {
124140
const user = await User.findOne({ refreshToken: token });
125141
if (!user) return res.status(403).json({ message: 'Invalid refresh token' });
126142

127-
jwt.verify(token, process.env.JWT_REFRESH_SECRET, (err, decoded) => {
143+
jwt.verify(token, JWT_REFRESH_SECRET, (err, decoded) => {
128144
if (err || decoded.id !== user._id.toString()) {
129145
return res.status(403).json({ message: 'Invalid or expired refresh token' });
130146
}
131147

132-
const accessToken = generateTokens(user);
133-
res.json({ accessToken });
148+
const { accessToken, refreshToken: newRefreshToken } = generateTokens(user);
149+
user.refreshToken = newRefreshToken;
150+
user.save();
151+
152+
res.json({ accessToken, refreshToken: newRefreshToken });
134153
});
135154
} catch (err) {
136-
res.status(500).json({ message: 'Server error'});
137-
console.error('Refresh token error:', err);
155+
console.error('REFRESH ERROR:', err);
156+
res.status(500).json({ message: 'Server error' });
138157
}
139158
};
140159

141-
142-
143-
144-
145-
//temporary dev registration endpoint
146-
// 🚨 Only for initial setup — REMOVE after first use
160+
// === DEV REGISTER ===
147161
export const devRegister = async (req, res) => {
148162
try {
149-
const existingAdmin = await User.findOne({ email: 'admin@vision.pk' });
163+
const existingAdmin = await User.findOne({ email: 'sabbbas.a30@gmail.com' });
150164
if (existingAdmin) {
151165
return res.status(400).json({ message: 'Admin already exists' });
152166
}
153167

154-
const hashedPassword = await bcrypt.hash('Admin123!', 12); // choose your own secure default
168+
const hashedPassword = await bcrypt.hash('Admin123!', 12);
155169

156170
const admin = new User({
157171
fullName: 'Sabbas Ahmad',
158172
email: 'sabbbas.a30@gmail.com',
159173
password: hashedPassword,
160174
role: 'admin',
161-
permissions: PERMISSIONS, // give all possible permissions initially
175+
permissions: PERMISSIONS,
162176
addedBy: null,
163177
});
164178

165179
await admin.save();
166-
167180
res.status(201).json({ message: 'Initial admin created', email: admin.email });
168181
} catch (err) {
182+
console.error("DEV REGISTER ERROR:", err);
169183
res.status(500).json({ message: 'Failed to create admin', error: err.message });
170184
}
171185
};
172-
// 🚨 Only for initial setup — REMOVE after first use

app/logs/auth.log

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{"level":"error","message":"Login failed secretOrPrivateKey must have a value","stack":"Error: secretOrPrivateKey must have a value\n at module.exports [as sign] (C:\\Users\\DELL\\Desktop\\VisualIndex Backend\\app\\node_modules\\jsonwebtoken\\sign.js:111:20)\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:27:27)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:84:43)"}
2+
{"level":"info","message":"User logout attempt"}
3+
{"level":"error","message":"Login failed secretOrPrivateKey must have a value","stack":"Error: secretOrPrivateKey must have a value\n at module.exports [as sign] (C:\\Users\\DELL\\Desktop\\VisualIndex Backend\\app\\node_modules\\jsonwebtoken\\sign.js:111:20)\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:27:27)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:84:43)"}
4+
{"level":"error","message":"Login failed secretOrPrivateKey must have a value","stack":"Error: secretOrPrivateKey must have a value\n at module.exports [as sign] (C:\\Users\\DELL\\Desktop\\VisualIndex Backend\\app\\node_modules\\jsonwebtoken\\sign.js:111:20)\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:27:27)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:84:43)"}
5+
{"level":"error","message":"Login failed secretOrPrivateKey must have a value","stack":"Error: secretOrPrivateKey must have a value\n at module.exports [as sign] (C:\\Users\\DELL\\Desktop\\VisualIndex Backend\\app\\node_modules\\jsonwebtoken\\sign.js:111:20)\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:27:27)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:84:43)"}
6+
{"level":"error","message":"Login failed secretOrPrivateKey must have a value","stack":"Error: secretOrPrivateKey must have a value\n at module.exports [as sign] (C:\\Users\\DELL\\Desktop\\VisualIndex Backend\\app\\node_modules\\jsonwebtoken\\sign.js:111:20)\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:27:27)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:84:43)"}
7+
{"level":"error","message":"Login failed accessToken is not defined","stack":"ReferenceError: accessToken is not defined\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:29:12)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:86:43)"}
8+
{"level":"error","message":"Login failed accessToken is not defined","stack":"ReferenceError: accessToken is not defined\n at generateTokens (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:32:12)\n at login (file:///C:/Users/DELL/Desktop/VisualIndex%20Backend/app/controllers/authController.js:89:43)"}
9+
{"level":"info","message":"User registered: viewer@test.com by 689530bbc1d8d5095e3b47f5"}
10+
{"level":"info","message":"User logout attempt"}
11+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
12+
{"level":"info","message":"✅ User login: viewer@test.com"}
13+
{"level":"info","message":"User logout attempt"}
14+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
15+
{"level":"info","message":"✅ User registered: viewer@test.com by 68954bf93b3dcab949394620"}
16+
{"level":"info","message":"✅ User login: viewer@test.com"}
17+
{"level":"info","message":"User logout attempt"}
18+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
19+
{"level":"info","message":"✅ User login: sabbbas.a30@gmail.com"}
20+
{"level":"info","message":"✅ User registered: viewer_1754615267686@test.com by 68954de468228c1b5a79ecaa"}
21+
{"level":"info","message":"✅ User login: viewer_1754615267686@test.com"}
22+
{"level":"info","message":"User logout attempt"}

app/package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
"name": "vision-index",
33
"version": "1.0.0",
44
"main": "server.js",
5+
"type": "module",
56
"scripts": {
67
"start": "node server.js",
78
"dev": "nodemon server.js",
89
"lint": "eslint . --ext .js",
9-
"test": "echo \"Error: no test specified\" && exit 1"
10+
"test": "mocha \"tests/**/*.js\" --timeout 10000"
1011
},
11-
"type": "module",
1212
"keywords": [
1313
"visionindex",
1414
"facial recognition",
@@ -43,6 +43,7 @@
4343
"description": "Backend for VisionIndex – a post-event facial recognition system that analyzes CCTV recordings to track and log individual presence across institutional zones for attendance and security auditing.",
4444
"dependencies": {
4545
"bcrypt": "^6.0.0",
46+
"chai-http": "^5.1.2",
4647
"cors": "^2.8.5",
4748
"dotenv": "^17.2.1",
4849
"express": "^5.1.0",
@@ -53,9 +54,12 @@
5354
},
5455
"devDependencies": {
5556
"@eslint/js": "^9.32.0",
57+
"chai": "^5.2.1",
5658
"eslint": "^9.32.0",
5759
"eslint-config-prettier": "^10.1.8",
5860
"globals": "^16.3.0",
59-
"prettier": "^3.6.2"
61+
"mocha": "^11.7.1",
62+
"prettier": "^3.6.2",
63+
"supertest": "^7.1.4"
6064
}
6165
}

app/server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ const port = process.env.PORT || 3000;
3737
app.listen(port, () => {
3838
console.log(`Server is running on port ${port}`);
3939
});
40+
export default app; // 👈 Add this line

0 commit comments

Comments
 (0)