commit
3efa2503ea
8 changed files with 173 additions and 0 deletions
@ -0,0 +1,8 @@ |
|||||
|
PORT=3000 |
||||
|
JWT_SECRET=yourSuperSecretKey # Replace with your actual secret key |
||||
|
|
||||
|
# Database configuration |
||||
|
DB_HOST=localhost |
||||
|
DB_USER=root |
||||
|
DB_PASSWORD=yourpassword |
||||
|
DB_NAME=jwt_auth |
||||
@ -0,0 +1,4 @@ |
|||||
|
.env |
||||
|
node_modules |
||||
|
package-lock.json |
||||
|
.vscode |
||||
@ -0,0 +1,11 @@ |
|||||
|
const mysql = require('mysql2/promise'); |
||||
|
require('dotenv').config(); |
||||
|
|
||||
|
const pool = mysql.createPool({ |
||||
|
host: process.env.DB_HOST, |
||||
|
user: process.env.DB_USER, |
||||
|
password: process.env.DB_PASSWORD, |
||||
|
database: process.env.DB_NAME, |
||||
|
}); |
||||
|
|
||||
|
module.exports = pool; |
||||
@ -0,0 +1,15 @@ |
|||||
|
const express = require('express'); |
||||
|
const authRoutes = require('./routes/authRoute'); |
||||
|
const protectedRoutes = require('./routes/protectedRoute'); |
||||
|
require('dotenv').config(); |
||||
|
|
||||
|
const app = express(); |
||||
|
|
||||
|
app.use(express.json()); |
||||
|
|
||||
|
app.use('/api/auth', authRoutes); |
||||
|
app.use('/api/protected', protectedRoutes); |
||||
|
|
||||
|
app.listen(process.env.PORT, () => { |
||||
|
console.log(`Server running on port ${process.env.PORT}`); |
||||
|
}); |
||||
@ -0,0 +1,42 @@ |
|||||
|
const jwt = require('jsonwebtoken'); |
||||
|
require('dotenv').config(); |
||||
|
|
||||
|
const activeSessions = {}; // store last activity timestamp for tokens
|
||||
|
|
||||
|
module.exports = (requiredRole = null) => { |
||||
|
return (req, res, next) => { |
||||
|
const authHeader = req.headers.authorization; |
||||
|
|
||||
|
if (!authHeader) { |
||||
|
return res.status(401).json({ message: 'No token provided' }); |
||||
|
} |
||||
|
|
||||
|
const token = authHeader.split(' ')[1]; |
||||
|
|
||||
|
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { |
||||
|
if (err) { |
||||
|
return res.status(401).json({ message: 'Invalid token' }); |
||||
|
} |
||||
|
|
||||
|
// Check token last activity
|
||||
|
const lastActivity = activeSessions[token]; |
||||
|
const now = Date.now(); |
||||
|
|
||||
|
if (lastActivity && now - lastActivity > 30 * 60 * 1000) { |
||||
|
delete activeSessions[token]; |
||||
|
return res.status(401).json({ message: 'Token expired due to inactivity' }); |
||||
|
} |
||||
|
|
||||
|
// Update last activity
|
||||
|
activeSessions[token] = now; |
||||
|
|
||||
|
req.user = decoded; |
||||
|
|
||||
|
if (requiredRole && decoded.role !== requiredRole) { |
||||
|
return res.status(403).json({ message: 'Forbidden. Insufficient role' }); |
||||
|
} |
||||
|
|
||||
|
next(); |
||||
|
}); |
||||
|
}; |
||||
|
}; |
||||
@ -0,0 +1,24 @@ |
|||||
|
{ |
||||
|
"name": "api-isakafo", |
||||
|
"version": "1.0.0", |
||||
|
"main": "index.js", |
||||
|
"scripts": { |
||||
|
"test": "echo \"Error: no test specified\" && exit 1", |
||||
|
"start": "node index.js", |
||||
|
"dev": "nodemon index.js" |
||||
|
}, |
||||
|
"keywords": [], |
||||
|
"author": "C4M-ONG-ADM", |
||||
|
"license": "ISC", |
||||
|
"description": "backend API for Isakafo", |
||||
|
"dependencies": { |
||||
|
"bcryptjs": "^3.0.2", |
||||
|
"dotenv": "^17.0.0", |
||||
|
"express": "^5.1.0", |
||||
|
"jsonwebtoken": "^9.0.2", |
||||
|
"mysql2": "^3.14.1" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"nodemon": "^3.1.10" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
const express = require('express'); |
||||
|
const bcrypt = require('bcryptjs'); |
||||
|
const jwt = require('jsonwebtoken'); |
||||
|
const pool = require('../config/databases'); |
||||
|
require('dotenv').config(); |
||||
|
|
||||
|
const router = express.Router(); |
||||
|
|
||||
|
router.post('/login', async (req, res) => { |
||||
|
const { username, password } = req.body; |
||||
|
|
||||
|
try { |
||||
|
const [rows] = await pool.query( |
||||
|
'SELECT * FROM users WHERE username = ?', |
||||
|
[username] |
||||
|
); |
||||
|
|
||||
|
if (rows.length === 0) { |
||||
|
return res.status(401).json({ message: 'Invalid credentials' }); |
||||
|
} |
||||
|
|
||||
|
const user = rows[0]; |
||||
|
|
||||
|
const isMatch = await bcrypt.compare(password, user.password); |
||||
|
|
||||
|
if (!isMatch) { |
||||
|
return res.status(401).json({ message: 'Invalid credentials' }); |
||||
|
} |
||||
|
|
||||
|
const payload = { |
||||
|
id: user.id, |
||||
|
username: user.username, |
||||
|
role: user.role, |
||||
|
}; |
||||
|
|
||||
|
const token = jwt.sign(payload, process.env.JWT_SECRET, { |
||||
|
expiresIn: '2h', // max lifespan
|
||||
|
}); |
||||
|
|
||||
|
res.json({ token }); |
||||
|
} catch (err) { |
||||
|
console.error(err); |
||||
|
res.status(500).json({ message: 'Server error' }); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
module.exports = router; |
||||
@ -0,0 +1,22 @@ |
|||||
|
const express = require('express'); |
||||
|
const authMiddleware = require('../middleware/authMiddleware'); |
||||
|
|
||||
|
const router = express.Router(); |
||||
|
|
||||
|
// Open only to logged users
|
||||
|
router.get('/profile', authMiddleware(), (req, res) => { |
||||
|
res.json({ |
||||
|
message: 'Welcome to your profile!', |
||||
|
user: req.user, |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// Open only to admins
|
||||
|
router.get('/admin', authMiddleware('admin'), (req, res) => { |
||||
|
res.json({ |
||||
|
message: 'Welcome, admin!', |
||||
|
user: req.user, |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
module.exports = router; |
||||
Loading…
Reference in new issue