From a288c25561e6542c82fc6ae69062a1b20dd4182f Mon Sep 17 00:00:00 2001 From: Pau Date: Sun, 6 Apr 2025 18:20:47 +0200 Subject: [PATCH] added refresh token for modulized authorization --- .../authorization/refreshTokenController.js | 52 +++++++++++++++++++ src/middlewares/auth.js | 13 +++-- src/services/authService.js | 19 ++++++- 3 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 src/controllers/authorization/refreshTokenController.js diff --git a/src/controllers/authorization/refreshTokenController.js b/src/controllers/authorization/refreshTokenController.js new file mode 100644 index 0000000..30ea701 --- /dev/null +++ b/src/controllers/authorization/refreshTokenController.js @@ -0,0 +1,52 @@ +import { getUserListModel } from "../../models/authorization/userModel.js" +import { sendResponseUnauthorized } from "../../utils/responses.js" +import { noResults } from "../../validators/result-validators.js" +import mysql from "../../adapters/mysql.js" +import { error401, errorHandler } from "../../utils/errors.js" +import { generateTokens } from "../../services/authService.js" +import dotenv from "dotenv" +dotenv.config(); + +const postRefreshTokenController = (req, res, next, config) => { + const conn = mysql.start(config) + const { user } = req.auth + + getUserListModel({ uuid: user }) + .then((response) => { + if (noResults(response)) { + const err = error401() + const error = errorHandler(err, config.environment) + return sendResponseUnauthorized(res, error) + } + + // only include uuid and username in the response + const user = response.map(({ id, password, createdBy, deleted, deletedBy, ...rest }) => rest) + + return getUserListModel({ uuid: user.uuid, conn }) + .then((response) => { + if (noResults(response)) { + const err = error401() + const error = errorHandler(err, config.environment) + return sendResponseUnauthorized(res, error) + } + + const tokenPayload = { + uuid: response[0].uuid, + role: response[0].role + } + + const {token, refreshToken} = generateTokens(tokenPayload) + //token application in frontend + next({token, refreshToken}) + }) + }) + .catch((err) => { + const error = errorHandler(err, config.environment) + return res.status(error.code).json(error) + }) + .finally(() => { + mysql.end(conn) + }) +} + +export { postRefreshTokenController } \ No newline at end of file diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index d1dba4e..0628622 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -1,10 +1,9 @@ -import { sendResponseAccessDenied } from '../utils/responses.js'; -import { checkPermission, getDataFromToken, generateAccessToken} from '../services/authService.js'; +import { sendResponseAccessDenied, sendResponseUnauthorized, sendResponseNotFound } from '../utils/responses.js'; +import { checkPermission, getDataFromToken, generateTokens} from '../services/authService.js'; import { getRolesHasPermissionsModel } from '../models/authorization/roles_has_permissionsModel.js'; import { error404, errorHandler } from '../utils/errors.js'; import { noResults } from '../validators/result-validators.js'; import mysql from '../adapters/mysql.js'; -import { sendResponseNotFound } from '../utils/responses.js'; const obtainToken = (req, res) => { return new Promise((resolve, reject) => { @@ -12,7 +11,7 @@ const obtainToken = (req, res) => { if (token) { resolve(token); } else { - return sendResponseAccessDenied(res, { message: 'No authorization provided. Access token required' }) + return sendResponseUnauthorized(res, { message: 'No authorization provided. Access token required' }) } }); }; @@ -20,8 +19,8 @@ const obtainToken = (req, res) => { const setToken = (result, req, res, next, config) => { const { uuid, role } = result._data const payload = {role, user: uuid} - const token = generateAccessToken(payload) - next({ user: { ...result, token } }) + const {accessToken, refreshToken} = generateTokens(payload) + next({ user: { ...result, accessToken, refreshToken} }) } const authenticateToken = (req, res, next) => { @@ -32,7 +31,7 @@ const authenticateToken = (req, res, next) => { next(); }) .catch((error) => { - return sendResponseAccessDenied(res, error); + return sendResponseUnauthorized(res, error); }); }; diff --git a/src/services/authService.js b/src/services/authService.js index aeed91b..45b6dcb 100644 --- a/src/services/authService.js +++ b/src/services/authService.js @@ -28,11 +28,26 @@ const getDataFromToken = (token) => { * @returns {string} - The generated JWT token. */ const generateAccessToken = (payload) => { + const contents = { ...payload, type: 'access' }; const JWT_SECRET = process.env.JWT_SECRET; const JWT_TIME = parseInt(process.env.JWT_TIME, 10); - return jwt.sign(payload, JWT_SECRET, JWT_TIME ? { expiresIn: JWT_TIME } : {}); + return jwt.sign(contents, JWT_SECRET, JWT_TIME ? { expiresIn: JWT_TIME } : {}); }; +const generateRefreshToken = (payload) => { + const contents = { ...payload, type: 'refresh' }; + const JWT_SECRET = process.env.JWT_SECRET + const JWT_TIME = parseInt(process.env.JWT_REFRESH_TIME, 10); + return jwt.sign(contents, JWT_SECRET, JWT_TIME ? { expiresIn: JWT_TIME } : {}); +} + +const generateTokens = (payload) => { + const accessToken = generateAccessToken(payload); + const refreshToken = generateRefreshToken(payload); + + return { accessToken, refreshToken }; +} + /** * Checks if a user has permission to perform an action on a resource. * @param {string} userId - The ID of the user. @@ -82,6 +97,6 @@ const _getEndpointByRoute = (route, config) => { export { getDataFromToken, - generateAccessToken, + generateTokens, checkPermission, }; \ No newline at end of file