├── .gitignore ├── logs ├── application-2024-04-08.log ├── .6de05a4169ce4b4e664365a5a798cbc325f986ae-audit.json └── application-2024-04-07.log ├── config.zip ├── .ebextensions ├── validations ├── subChapterReadValidation.js ├── notesValidation.js ├── appUsersValidation.js ├── likesValidation.js ├── chaptersValidation.js ├── subChaptersValidation.js └── progressValidation.js ├── models ├── refreshToken.js └── appUsers.js ├── routes ├── authRoutes.js └── appUserRoutes.js ├── logs\.6de05a4169ce4b4e664365a5a798cbc325f986ae-audit.json ├── middleware └── authenticateToken.js ├── logger.js ├── config └── database.js ├── swaggerConfig.js ├── index.js ├── package.json ├── app.js ├── controllers ├── appusersController.js └── authController.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /logs/application-2024-04-08.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanchit0496/nodejs_jwt_authentication/HEAD/config.zip -------------------------------------------------------------------------------- /.ebextensions: -------------------------------------------------------------------------------- 1 | commands: 2 | setvars: 3 | command: /opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""' > /etc/profile.d/sh.local 4 | packages: 5 | yum: 6 | jq: [] -------------------------------------------------------------------------------- /validations/subChapterReadValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const createSubchapterReadSchema = Joi.object({ 4 | progressId: Joi.number().integer().required(), 5 | subchapterId: Joi.string().guid({ version: ['uuidv4'] }).required() 6 | // Add other fields as necessary 7 | }); 8 | 9 | module.exports = { 10 | createSubchapterReadSchema, 11 | }; 12 | -------------------------------------------------------------------------------- /models/refreshToken.js: -------------------------------------------------------------------------------- 1 | const { DataTypes } = require('sequelize'); 2 | const sequelize = require('../config/database'); 3 | const AppUser = require('./appUsers'); 4 | 5 | const RefreshToken = sequelize.define('RefreshToken', { 6 | token: DataTypes.STRING, 7 | expiryDate: DataTypes.DATE, 8 | }); 9 | 10 | // Define relationships 11 | 12 | module.exports = RefreshToken; 13 | -------------------------------------------------------------------------------- /routes/authRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const authController = require('../controllers/authController'); 3 | const router = express.Router(); 4 | 5 | // Authentication routes 6 | router.post('/login', authController.login); 7 | router.post('/token', authController.refreshToken); 8 | router.delete('/logout', authController.logout); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /validations/notesValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const createNotesSchema = Joi.object({ 4 | userId: Joi.string().guid({ version: ['uuidv4'] }).required(), 5 | note: Joi.string().allow('', null), 6 | subchapterUUID: Joi.string().guid({ version: ['uuidv4'] }).required() 7 | // Add other fields as necessary 8 | }); 9 | 10 | module.exports = { 11 | createNotesSchema, 12 | }; 13 | -------------------------------------------------------------------------------- /validations/appUsersValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const createUserSchema = Joi.object({ 4 | userId: Joi.string().required(), 5 | username: Joi.string().min(3).max(255).required(), 6 | email: Joi.string().email().required(), 7 | mobile: Joi.string().allow('', null), 8 | displayPicture: Joi.string().allow('', null) 9 | }); 10 | 11 | module.exports = { 12 | createUserSchema, 13 | }; 14 | -------------------------------------------------------------------------------- /validations/likesValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const createLikeSchema = Joi.object({ 4 | userId: Joi.string().guid({ version: ['uuidv4'] }).required(), 5 | chapterId: Joi.string().guid({ version: ['uuidv4'] }).required(), 6 | subchapterId: Joi.string().guid({ version: ['uuidv4'] }).required() 7 | // Add other fields as necessary 8 | }); 9 | 10 | module.exports = { 11 | createLikeSchema, 12 | }; 13 | -------------------------------------------------------------------------------- /validations/chaptersValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const createChapterSchema = Joi.object({ 4 | chapterNumber: Joi.string().max(10).required(), 5 | chapterTitleSanskrit: Joi.string().allow('', null).required(), 6 | chapterTitleHindi: Joi.string().allow('', null).required(), 7 | chapterTitleEnglish: Joi.string().allow('', null).required() 8 | // Add other fields as necessary 9 | }); 10 | 11 | module.exports = { 12 | createChapterSchema, 13 | }; 14 | -------------------------------------------------------------------------------- /logs/.6de05a4169ce4b4e664365a5a798cbc325f986ae-audit.json: -------------------------------------------------------------------------------- 1 | { 2 | "keep": { 3 | "days": true, 4 | "amount": 14 5 | }, 6 | "auditLog": "logs\\.6de05a4169ce4b4e664365a5a798cbc325f986ae-audit.json", 7 | "files": [ 8 | { 9 | "date": 1712487639893, 10 | "name": "logs\\application-2024-04-07.log", 11 | "hash": "93e49aee085f65381afa417f58e05893e379f6a06bddb2c67bfbd139865a881f" 12 | } 13 | ], 14 | "hashType": "sha256" 15 | } -------------------------------------------------------------------------------- /validations/subChaptersValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | const createSubchapterSchema = Joi.object({ 4 | chapterId: Joi.string().guid({ version: ['uuidv4'] }).required(), 5 | subChapterContentEnglish: Joi.string().allow('', null), 6 | subChapterContentHindi: Joi.string().allow('', null), 7 | subChapterContentSanskrit: Joi.string().allow('', null) 8 | // Add other fields as necessary 9 | }); 10 | 11 | module.exports = { 12 | createSubchapterSchema, 13 | }; 14 | -------------------------------------------------------------------------------- /logs\.6de05a4169ce4b4e664365a5a798cbc325f986ae-audit.json: -------------------------------------------------------------------------------- 1 | { 2 | "keep": { 3 | "days": true, 4 | "amount": 14 5 | }, 6 | "auditLog": "logs\\.6de05a4169ce4b4e664365a5a798cbc325f986ae-audit.json", 7 | "files": [ 8 | { 9 | "date": 1712487639893, 10 | "name": "logs\\application-2024-04-07.log", 11 | "hash": "93e49aee085f65381afa417f58e05893e379f6a06bddb2c67bfbd139865a881f" 12 | }, 13 | { 14 | "date": 1712573926471, 15 | "name": "logs/application-2024-04-08.log", 16 | "hash": "34589f31ac828e7acf11beabe6ab4c566e8bd33e49dee1fb73bcb325d9ff3833" 17 | } 18 | ], 19 | "hashType": "sha256" 20 | } -------------------------------------------------------------------------------- /middleware/authenticateToken.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | require('dotenv').config(); 3 | 4 | const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET || 'your_access_token_secret'; // Should be an environment variable 5 | 6 | 7 | const authenticateToken = (req, res, next) => { 8 | const authHeader = req.headers['authorization']; 9 | const token = authHeader && authHeader.split(' ')[1]; 10 | 11 | if (token == null) return res.sendStatus(401); 12 | 13 | jwt.verify(token, ACCESS_TOKEN_SECRET, (err, user) => { 14 | console.log('error', err) 15 | if (err) return res.sendStatus(403); 16 | req.user = user; 17 | next(); 18 | }); 19 | }; 20 | 21 | module.exports = authenticateToken; 22 | -------------------------------------------------------------------------------- /logger.js: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | require('winston-daily-rotate-file'); 3 | 4 | const logConfiguration = { 5 | transports: [ 6 | new winston.transports.DailyRotateFile({ 7 | filename: './logs/application-%DATE%.log', 8 | datePattern: 'YYYY-MM-DD', 9 | zippedArchive: true, 10 | maxSize: '20m', 11 | maxFiles: '14d' 12 | }) 13 | ], 14 | format: winston.format.combine( 15 | winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), 16 | winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`) 17 | ) 18 | }; 19 | 20 | const logger = winston.createLogger(logConfiguration); 21 | 22 | module.exports = logger; 23 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | require('dotenv').config(); 3 | 4 | const DB_HOST = process.env.DB_HOST || "localhost"; // Default to localhost if not specified 5 | const DB_PORT = process.env.DB_PORT || 3306; // Default MySQL port is 3306 6 | const DB_USERNAME = "root"; // Ensure this is set in your .env file 7 | const DB_PASSWORD = process.env.DB_PASSWORD || ""; // Default to empty if not specified 8 | const DB_NAME = "application"; // Ensure this is set in your .env file 9 | 10 | const sequelize = new Sequelize(DB_NAME, DB_USERNAME, DB_PASSWORD, { 11 | host: DB_HOST, 12 | port: DB_PORT, 13 | dialect: 'mysql', // or another dialect if you're not using MySQL 14 | }); 15 | 16 | 17 | module.exports = sequelize; 18 | -------------------------------------------------------------------------------- /swaggerConfig.js: -------------------------------------------------------------------------------- 1 | // swaggerConfig.js 2 | 3 | const swaggerJsDoc = require('swagger-jsdoc'); 4 | const swaggerUi = require('swagger-ui-express'); 5 | 6 | const swaggerOptions = { 7 | definition: { 8 | openapi: '3.0.0', 9 | info: { 10 | title: 'Users Service API', 11 | version: '1.0.0', 12 | description: 'A Users Microservice API documentation', 13 | }, 14 | servers: [ 15 | { 16 | url: 'http://localhost:3000', // Replace with your server's URL and port 17 | description: 'Development server', 18 | }, 19 | ], 20 | }, 21 | apis: ['./routes/*.js'], // Path to the API docs 22 | }; 23 | 24 | // Initialize swagger-jsdoc 25 | const swaggerSpec = swaggerJsDoc(swaggerOptions); 26 | 27 | module.exports = { swaggerSpec, swaggerUi }; 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app'); 2 | const sequelize = require('./config/database'); 3 | require('dotenv').config(); 4 | 5 | const PORT = process.env.RDS_PORT || 8080; 6 | console.log('process.env.RDS_PORT', process.env.RDS_PORT) 7 | 8 | 9 | sequelize.sync({ alter: true }) // This will check what is the current state of the table in the database (which columns it has, what are their data types, etc), and then perform the necessary changes in the table to make it match the model. 10 | .then(() => { 11 | console.log('Database synced'); 12 | // ... start your server here 13 | app.listen(PORT, () => { 14 | console.log(`Server running on RDS_PORT ${PORT}`); 15 | }); 16 | }) 17 | .catch((err) => { 18 | console.error('Error syncing database:', err); 19 | }); 20 | -------------------------------------------------------------------------------- /validations/progressValidation.js: -------------------------------------------------------------------------------- 1 | const Joi = require("joi"); 2 | 3 | // Validation for creating a new user 4 | const createUserSchema = Joi.object({ 5 | userId: Joi.string().guid({ version: ['uuidv4'] }).required(), 6 | username: Joi.string().min(3).max(255).required(), 7 | email: Joi.string().email().required(), 8 | mobile: Joi.string().allow('', null), 9 | displayPicture: Joi.string().allow('', null) 10 | // Add other fields as necessary 11 | }); 12 | 13 | // Validation for creating a new progress entry 14 | const createProgressSchema = Joi.object({ 15 | userId: Joi.string().guid({ version: ['uuidv4'] }).required(), 16 | chapterUUID: Joi.string().guid({ version: ['uuidv4'] }).required() 17 | // Add other fields as necessary 18 | }); 19 | 20 | module.exports = { 21 | createUserSchema, 22 | createProgressSchema, 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs_scaffolding", 3 | "version": "1.0.0", 4 | "description": "a nodejs starter code", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "pm2 start index.js --name 'nodejs_scaffolding'", 9 | "dev": "nodemon index.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^5.1.1", 15 | "dotenv": "^16.4.1", 16 | "express": "^4.18.2", 17 | "ioredis": "^5.3.2", 18 | "joi": "^17.11.0", 19 | "jsonwebtoken": "^9.0.2", 20 | "kafkajs": "^2.2.4", 21 | "mysql2": "^3.7.0", 22 | "pm2": "^5.3.0", 23 | "sequelize": "^6.35.2", 24 | "swagger-jsdoc": "^6.2.8", 25 | "swagger-ui-express": "^5.0.0", 26 | "winston": "^3.11.0", 27 | "winston-daily-rotate-file": "^4.7.1" 28 | }, 29 | "devDependencies": { 30 | "nodemon": "^3.0.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const logger = require("./logger"); // Make sure the path is correct 4 | const userRoutes = require('./routes/appUserRoutes'); 5 | const authRoutes = require('./routes/authRoutes'); 6 | const { swaggerSpec, swaggerUi } = require('./swaggerConfig'); // Adjust the path if necessary 7 | 8 | app.use(express.json()); // For parsing application/json 9 | 10 | // Swagger route 11 | app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); 12 | 13 | app.use((req, res, next) => { 14 | // Log the request method and URL 15 | logger.info(`Incoming request: ${req.method} ${req.url} Body: ${JSON.stringify(req.body)}`); 16 | next(); 17 | }); 18 | 19 | // Use the user routes for any requests to '/users' 20 | app.use('/appusers', userRoutes); 21 | app.use('/api/auth', authRoutes); 22 | 23 | 24 | // Export the app for use in index.js 25 | module.exports = app; 26 | -------------------------------------------------------------------------------- /models/appUsers.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const sequelize = require('../config/database'); // Make sure this points to your actual database configuration file 3 | 4 | const AppUser = sequelize.define('AppUser', { 5 | userId: { 6 | type: Sequelize.STRING, 7 | allowNull: false, 8 | primaryKey: true 9 | }, 10 | username: { 11 | type: Sequelize.STRING, 12 | allowNull: false 13 | }, 14 | password: { 15 | type: Sequelize.STRING, 16 | allowNull: false 17 | }, 18 | email: { 19 | type: Sequelize.STRING, 20 | allowNull: false, 21 | validate: { 22 | isEmail: true 23 | } 24 | }, 25 | mobile: { 26 | type: Sequelize.STRING, 27 | allowNull: true // Assuming that mobile is not required 28 | }, 29 | displayPicture: { 30 | type: Sequelize.TEXT, 31 | allowNull: true // Assuming that display picture is not required 32 | } 33 | }, { 34 | // Sequelize options 35 | tableName: 'appusers', 36 | charset: 'utf8mb4', 37 | collate: 'utf8mb4_unicode_ci' 38 | }); 39 | 40 | module.exports = AppUser; 41 | -------------------------------------------------------------------------------- /routes/appUserRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const userController = require('../controllers/appusersController'); 4 | const { createUserSchema } = require('../validations/appUsersValidation'); 5 | const authenticateToken = require('../middleware/authenticateToken') 6 | 7 | /** 8 | * @swagger 9 | * components: 10 | * schemas: 11 | * User: 12 | * type: object 13 | * required: 14 | * - name 15 | * - email 16 | * properties: 17 | * id: 18 | * type: string 19 | * description: The auto-generated id of the user 20 | * name: 21 | * type: string 22 | * description: The name of the user 23 | * email: 24 | * type: string 25 | * description: The email of the user 26 | * example: 27 | * id: d5fE_asz 28 | * name: John Doe 29 | * email: johndoe@example.com 30 | * 31 | * /users: 32 | * get: 33 | * summary: Returns a list of users 34 | * responses: 35 | * 200: 36 | * description: The list of the users 37 | * content: 38 | * application/json: 39 | * schema: 40 | * type: array 41 | * items: 42 | * $ref: '#/components/schemas/User' 43 | */ 44 | 45 | // Middleware for user validation 46 | const validateUserCreation = (req, res, next) => { 47 | const { error } = createUserSchema.validate(req.body); 48 | if (error) { 49 | return res.status(400).json({ message: error.details[0].message }); 50 | } 51 | next(); 52 | }; 53 | 54 | // Define the CRUD routes for users 55 | router.get('/:id', authenticateToken, userController.getAppUserById); 56 | router.post('/', userController.createAppUser); 57 | router.put('/:id', authenticateToken, userController.updateAppUser); 58 | router.delete('/:id', authenticateToken, userController.deleteAppUser); 59 | router.get('/', authenticateToken, userController.getAllAppUsers); 60 | 61 | module.exports = router; -------------------------------------------------------------------------------- /controllers/appusersController.js: -------------------------------------------------------------------------------- 1 | const AppUser = require('../models/appUsers'); // Ensure this path points to your AppUser model 2 | const bcrypt = require('bcrypt'); 3 | 4 | exports.createAppUser = async (req, res) => { 5 | try { 6 | // Hash the password before saving 7 | const hashedPassword = await bcrypt.hash(req.body.password, 10); // 10 rounds 8 | const newUserDetails = { ...req.body, password: hashedPassword }; 9 | 10 | const newUser = await AppUser.create(newUserDetails); 11 | 12 | // Respond with the new user object (excluding password) and tokens 13 | const { password, ...userWithoutPassword } = newUser.dataValues; 14 | 15 | res.status(201).json({ 16 | user: userWithoutPassword, 17 | }); 18 | } catch (err) { 19 | console.error(err); 20 | res.status(400).json(err); 21 | } 22 | }; 23 | 24 | exports.getAppUserById = async (req, res) => { 25 | const userId = req.params.id; 26 | 27 | try { 28 | const user = await AppUser.findByPk(userId); 29 | if (user) { 30 | res.json(user); 31 | } else { 32 | res.status(404).json({ message: 'User not found' }); 33 | } 34 | } catch (error) { 35 | res.status(500).json({ message: 'Internal Server Error', error }); 36 | } 37 | }; 38 | 39 | exports.updateAppUser = async (req, res) => { 40 | const userId = req.params.id; 41 | 42 | try { 43 | const [updated] = await AppUser.update(req.body, { where: { userId: userId } }); 44 | if (updated) { 45 | const updatedUser = await AppUser.findByPk(userId); 46 | res.status(200).json(updatedUser); 47 | } else { 48 | res.status(404).json({ message: 'User not found' }); 49 | } 50 | } catch (error) { 51 | res.status(400).json(error); 52 | } 53 | }; 54 | 55 | exports.deleteAppUser = async (req, res) => { 56 | try { 57 | const numberDestroyed = await AppUser.destroy({ where: { userId: req.params.id } }); 58 | if (numberDestroyed) { 59 | res.status(200).json({ message: 'User deleted successfully' }); 60 | } else { 61 | res.status(404).json({ message: 'User not found' }); 62 | } 63 | } catch (err) { 64 | res.status(400).json(err); 65 | } 66 | }; 67 | 68 | exports.getAllAppUsers = async (req, res) => { 69 | try { 70 | const users = await AppUser.findAll(); 71 | res.json(users); 72 | } catch (err) { 73 | res.status(400).json(err); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Node.js JWT Authentication Project 3 | 4 | This project provides a secure authentication system using JWT (JSON Web Tokens) for Node.js applications. It supports user registration, login, and securing routes that require user authentication. 5 | 6 | ## Getting Started 7 | 8 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. 9 | 10 | ### Prerequisites 11 | 12 | You need Node.js and a MySQL database installed on your system to run this project. 13 | 14 | ### Installation 15 | 16 | Clone the repository to your local machine: 17 | 18 | ```bash 19 | git clone https://github.com/yourusername/nodejs-jwt-auth.git 20 | cd nodejs-jwt-auth 21 | ``` 22 | 23 | Install the required dependencies: 24 | 25 | ```bash 26 | npm install 27 | ``` 28 | 29 | Set up your environment variables by creating a `.env` file in the root directory with the following content: 30 | 31 | ```env 32 | DB_HOST=localhost 33 | DB_USERNAME=your_database_username 34 | DB_PASSWORD=your_database_password 35 | DB_NAME=your_database_name 36 | ACCESS_TOKEN_SECRET=your_access_token_secret 37 | REFRESH_TOKEN_SECRET=your_refresh_token_secret 38 | ``` 39 | 40 | Run the application: 41 | 42 | ```bash 43 | npm start 44 | ``` 45 | 46 | ### Usage 47 | 48 | To **register** a new user, send a `POST` request to `/appusers` with a JSON body containing the username, email, password, and other information: 49 | 50 | ```json 51 | { 52 | "userId": "sdfsfsf", 53 | "username": "john_doe", 54 | "password": "password", 55 | "email": "john.doe@example.com", 56 | "mobile": "1234567890", 57 | "displayPicture": "https://example.com/profile-picture.jpg" 58 | } 59 | ``` 60 | 61 | To **login**, send a `POST` request to `api/auth/login` with the username and password: 62 | 63 | ```json 64 | { 65 | "username": "john_doe", 66 | "password": "password" 67 | } 68 | ``` 69 | 70 | To **update**, send a `PUT` request to `/appusers/{userId}` with the data that needs to be updated, send the access token as bearer token to avoid unauthorized error: 71 | 72 | ```json 73 | { 74 | "userId": "sdfsfsf", 75 | "username": "john_doey", 76 | "password": "password", 77 | "email": "john.doe@example.com", 78 | "mobile": "1234567890", 79 | "displayPicture": "https://example.com/profile-picture.jpg" 80 | } 81 | ``` 82 | 83 | To **get the new tokens from backend**, send a `POST` request to `api/auth/token` with the access token in body: 84 | 85 | ```json 86 | { 87 | "token": "Whgu2Yh7qp2UFmdkG8o28POAodA" 88 | } 89 | ``` 90 | 91 | On successful login, you will receive an `accessToken` and a `refreshToken`. 92 | 93 | To access protected routes, include the `accessToken` in the `Authorization` header as a Bearer token: 94 | 95 | ```plaintext 96 | Authorization: Bearer 97 | ``` 98 | 99 | When the `accessToken` expires, use the `refreshToken` to obtain a new `accessToken` by sending a `POST` request to `/api/users/token`: 100 | -------------------------------------------------------------------------------- /controllers/authController.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const bcrypt = require('bcrypt'); 3 | const AppUser = require('../models/appUsers') 4 | const RefreshToken = require('../models/refreshToken') 5 | 6 | const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET || 'your_access_token_secret'; // Should be an environment variable 7 | const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET || 'your_refresh_token_secret'; // Should be an environment variable 8 | 9 | // Helper function to generate an access token 10 | const generateAccessToken = (user) => { 11 | return jwt.sign({ userId: user.userId }, ACCESS_TOKEN_SECRET, { expiresIn: '15m' }); 12 | }; 13 | 14 | // Helper function to generate a refresh token 15 | const generateRefreshToken = (user) => { 16 | const refreshToken = jwt.sign({ userId: user.userId }, REFRESH_TOKEN_SECRET, { expiresIn: '7d' }); // Valid for 7 days 17 | return refreshToken; 18 | }; 19 | 20 | // Helper function to store refresh token in database 21 | const storeRefreshToken = async (userId, refreshToken) => { 22 | const expiryDate = new Date(); 23 | expiryDate.setDate(expiryDate.getDate() + 7); // 7 days from now 24 | 25 | return await RefreshToken.create({ 26 | userId: userId, 27 | token: refreshToken, 28 | expiryDate: expiryDate 29 | }); 30 | }; 31 | 32 | exports.login = async (req, res) => { 33 | try { 34 | const { username, password } = req.body; 35 | const user = await AppUser.findOne({ where: { username } }); 36 | 37 | if (!user) { 38 | return res.status(401).json({ message: "User not found" }); 39 | } 40 | 41 | const isMatch = await bcrypt.compare(password, user.password); 42 | if (!isMatch) { 43 | return res.status(401).json({ message: "Password is incorrect" }); 44 | } 45 | 46 | const accessToken = generateAccessToken(user); 47 | const refreshToken = generateRefreshToken(user); 48 | 49 | await storeRefreshToken(user.userId, refreshToken); 50 | 51 | res.json({ 52 | accessToken: accessToken, 53 | refreshToken: refreshToken 54 | }); 55 | } catch (error) { 56 | console.error(error); 57 | res.status(500).json({ message: "Internal Server Error" }); 58 | } 59 | }; 60 | 61 | exports.refreshToken = async (req, res) => { 62 | const { token } = req.body; 63 | 64 | if (!token) { 65 | return res.status(401).json({ message: "Refresh Token is required" }); 66 | } 67 | 68 | try { 69 | const refreshToken = await RefreshToken.findOne({ where: { token } }); 70 | if (!refreshToken) { 71 | return res.status(403).json({ message: "Refresh token is not in database" }); 72 | } 73 | 74 | if (refreshToken.expiryDate.getTime() < new Date().getTime()) { 75 | await refreshToken.destroy(); 76 | return res.status(403).json({ message: "Refresh token expired" }); 77 | } 78 | 79 | jwt.verify(token, REFRESH_TOKEN_SECRET, async (err, user) => { 80 | if (err) { 81 | return res.status(403).json({ message: "Invalid refresh token" }); 82 | } 83 | 84 | const newAccessToken = generateAccessToken(user); 85 | const newRefreshToken = generateRefreshToken(user); 86 | 87 | await refreshToken.update({ token: newRefreshToken }); 88 | 89 | res.json({ 90 | accessToken: newAccessToken, 91 | refreshToken: newRefreshToken 92 | }); 93 | }); 94 | } catch (error) { 95 | console.error(error); 96 | res.status(500).json({ message: "Internal Server Error" }); 97 | } 98 | }; 99 | 100 | exports.logout = async (req, res) => { 101 | const { token } = req.body; 102 | 103 | try { 104 | await RefreshToken.destroy({ where: { token } }); 105 | res.sendStatus(204); // Successfully processed the request, but not returning any content 106 | } catch (error) { 107 | console.error(error); 108 | res.status(500).json({ message: "Internal Server Error" }); 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /logs/application-2024-04-07.log: -------------------------------------------------------------------------------- 1 | 2024-04-07 17:27:05 info: Incoming request: GET /appusers/ Body: {} 2 | 2024-04-07 17:27:11 info: Incoming request: GET /appusers/ Body: {} 3 | 2024-04-07 17:27:26 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 4 | 2024-04-07 17:31:32 info: Incoming request: POST /api/auth/login Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 5 | 2024-04-07 17:31:55 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 6 | 2024-04-07 17:32:30 info: Incoming request: POST /api/auth/login Body: {"username":"john_doe","password":"password"} 7 | 2024-04-07 17:38:35 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 8 | 2024-04-07 17:39:29 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 9 | 2024-04-07 17:39:44 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 10 | 2024-04-07 17:40:20 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 11 | 2024-04-07 17:40:54 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 12 | 2024-04-07 17:41:41 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 13 | 2024-04-07 17:42:09 info: Incoming request: POST /api/auth/login Body: {"username":"john_doe","password":"password"} 14 | 2024-04-07 17:43:24 info: Incoming request: POST /appusers/ Body: {"userId":"sdfsfsf","username":"john_doe","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 15 | 2024-04-07 17:43:30 info: Incoming request: POST /api/auth/login Body: {"username":"john_doe","password":"password"} 16 | 2024-04-07 17:45:03 info: Incoming request: POST /api/auth/token Body: {"username":"john_doe","password":"password"} 17 | 2024-04-07 17:45:52 info: Incoming request: POST /api/auth/token Body: {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJzZGZzZnNmIiwiaWF0IjoxNzEyNDkyMDEwLCJleHAiOjE3MTMwOTY4MTB9.pP-l49UPuYcKU4TbWhgu2Yh7qp2UFmdkG8o28POAodA"} 18 | 2024-04-07 17:46:43 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 19 | 2024-04-07 17:47:49 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 20 | 2024-04-07 17:49:44 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 21 | 2024-04-07 17:50:03 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 22 | 2024-04-07 17:51:12 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 23 | 2024-04-07 17:51:43 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 24 | 2024-04-07 17:52:13 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 25 | 2024-04-07 17:53:08 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 26 | 2024-04-07 17:54:31 info: Incoming request: PUT /appusers/sdfsfsf Body: {"userId":"sdfsfsf","username":"john_doey","password":"password","email":"john.doe@example.com","mobile":"1234567890","displayPicture":"https://example.com/profile-picture.jpg"} 27 | --------------------------------------------------------------------------------