All files / ragchat-api/utils handleTokens.js

89.24% Statements 166/186
84% Branches 21/25
77.77% Functions 7/9
89.24% Lines 166/186

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 1861x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 3x 3x 5x 1x 1x 1x 1x 1x 1x 1x               1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 32x 32x 32x 32x 32x 32x 32x 32x 32x 26x 26x 26x 26x 26x 26x 26x 26x 20x 20x 20x 20x 20x 20x 26x 26x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 1x 3x 3x     3x
import jwt from 'jsonwebtoken';
import User from "../models/User.js";
import { findByIdAndUpdate, findOneAndUpdate, findOneRecord } from './handleDB.js';
import { performance } from "node:perf_hooks";
 
 
/**
 * generate a "oneTime" JWT, containing a given object
 * secret key & expire time come from .env
 * added user validation status to secret to invalidate this token after validating
 *
 * @param   {object}  payload  content of the token
 *
 * @return  {token}
 */
export const createVerificationToken = (payload) => {
  return jwt.sign({ id: payload.id, email: payload.email }, process.env.VERIFICATION_TOKEN_KEY, { expiresIn: process.env.VERIFICATION_TOKEN_TTL });
};
 
/**
 * verify that the Token exists and is untouched
 * expects req to contain the following
 * req.document to be a previously found user document
 * req.body.token to be the token to verify * 
 *
 * @param   req   request data
 * @param   res   response, sended to the user
 * @param   next  simply tell the code to go on with next function
 *
 */
export const verifyVerificationToken = async (req, res, next) => {
  // verify token
  const valid = jwt.verify(req.body.token, process.env.VERIFICATION_TOKEN_KEY, async (error, payload) => {
    // if invalid
    if (error) return res.status(498).json({ message: 'Token is no longer valid.' });
    // if valid
    req.body.email = payload.email;
    next();
  });
};
 
 
/**
 * generate a JWT, containing a given object
 * secret key & expire time come from .env
 *
 * @param   {object}  payload  content of the token
 *
 * @return  {token}
 */
export const createAccessToken = (payload) => {
  return jwt.sign(payload, process.env.JWT_SECRET_KEY, { expiresIn: process.env.JWT_TTL });
};
 
/**
 * generate a "oneTime" JWT, containing a given object
 * secret key & expire time come from .env
 * added user password to secret to invalidate this token after password change
 *
 * @param   {object}  payload  content of the token
 *
 * @return  {token}
 */
export const createPasswordToken = (payload) => {
  return jwt.sign({ id: payload.id, email: payload.email }, process.env.PASSWORD_TOKEN_KEY + payload.password, { expiresIn: process.env.PASSWORD_TOKEN_TTL });
};
 
 
/**
 * generate a JWT, containing a given object
 * secret key & expire time come from .env
 *
 * @param   {object}  payload  content of the token
 *
 * @return  {token}
 */
export const createRefreshToken = async (payload) => {
  try {
    // create
    const refreshToken = jwt.sign(payload, process.env.JWT_REFRESH_KEY);
    // save into user document
    await findByIdAndUpdate(User, payload.id, { refreshToken });
    // return
    return refreshToken;
  } catch (error) {
    throw error;
  }
};
 
 
/**
 * check if given refresh token exists in DB and is valid
 *
 * @param   {string}  refreshToken  refresh token
 *
 * @return  {any}         user from DB if exists, false if not
 */
export const verifyRefreshToken = async (refreshToken) => {
  try {
    // check if in DB
    const foundUser = await findOneRecord(User, { refreshToken });
    if (!foundUser) return false;
    // check if valid
    const user = jwt.verify(refreshToken, process.env.JWT_REFRESH_KEY);
    return (user && user.id === foundUser.id ? foundUser : false);
  } catch (error) {
    throw error;
  }
};
 
/**
 * delete given RefreshToken from user doc
 *
 * @param   {string}  refreshToken  hashed refresh token
 */
export const deleteRefreshToken = async (refreshToken) => {
  try {
    const user = await findOneAndUpdate(User, { refreshToken }, { $unset: { refreshToken: "" } });
    return;
  } catch (error) {
    throw error;
  }
};
 
 
/**
 * verify that the JWT exists and is untouched
 * If so, save user.id from token to res.currentUser for further computing
 *
 * @param   req   request data
 * @param   res   response, sended to the user
 * @param   next  simply tell the code to go on with next function
 *
 */
export const verifyAccessToken = async (req, res, next) => {
  performance.mark('verifyAccessToken:start');
 
  // define header
  const authHeader = req.headers['authorization'];
  // split token from authHeader - if available
  const token = authHeader && authHeader.split(' ')[1];
 
  // return if no token (or authHeader) found
  if (!token) return res.status(401).json({ message: 'No access token found. Access denied.' });
 
  // verify token
  performance.mark('jwt.verify:start');
  jwt.verify(token, process.env.JWT_SECRET_KEY, async (error, payload) => {
    performance.mark('jwt.verify:end');
 
    // if invalid
    if (error) return res.status(403).json({ message: 'Access token is no longer valid. Access denied.' });
    // if valid: remember current user id & role and go on
    global.currentUserId = payload.id;
    global.currentUserRole = payload.role;
    performance.mark('verifyAccessToken:end');
 
    next();
  });
};
 
 
/**
 * verify that the Token exists and is untouched
 * If so, save user.id from token to res.currentUser for further computing
 *
 * @param   req   request data
 * @param   res   response, sended to the user
 * @param   next  simply tell the code to go on with next function
 *
 */
export const verifyPasswordToken = async (req, res, next) => {
  try {
    // fetch user by token
    req.document = await findOneRecord(User, { resetPasswordToken: req.body.token }, '+password');  // verify token
    if (!req.document) return res.status(498).json({ message: 'Token is no longer valid.' });
    // check token validity
    jwt.verify(req.body.token, process.env.PASSWORD_TOKEN_KEY + req.document.password, async (error, payload) => {
      // if invalid
      if (error) return res.status(498).json({ message: 'Token is no longer valid.' });
      next();
    });
  } catch (error) {
    next(error);
  }
};