Skip to content
Snippets Groups Projects
Commit bf12b44f authored by Embruch, Gerd's avatar Embruch, Gerd
Browse files

finished testing route auth/refreshJWT

parent a68c9195
Branches
No related tags found
No related merge requests found
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`refresh JWT > given authtoken is valid > should respond with a proper body 1`] = `
{
"accessToken": Any<String>,
"message": "Access token refreshed",
}
`;
exports[`refresh JWT > given refresh token is invalid > should respond with a proper body 1`] = `
{
"message": "Refresh token is invalid",
}
`;
exports[`refresh JWT > given refresh token is malformed > should respond with a proper body 1`] = `
{
"message": "jwt malformed",
}
`;
exports[`refresh JWT > given refresh token is missing > should respond with a proper body 1`] = `
{
"message": "Refresh token is missing. Consider to re-login",
}
`;
...@@ -13,8 +13,6 @@ let response; ...@@ -13,8 +13,6 @@ let response;
// OBJECTS // OBJECTS
// ############################ // ############################
const mockedVals = vi.hoisted(() => { const mockedVals = vi.hoisted(() => {
// const password = await bcrypt.hash('StrongPass1!', Number(process.env.BCRYPT_STRENGTH));
return { return {
foundUser: { foundUser: {
_id: '66a29da2942b3eb', _id: '66a29da2942b3eb',
......
...@@ -2,108 +2,124 @@ ...@@ -2,108 +2,124 @@
import { vi, beforeAll, beforeEach, describe, expect, expectTypeOf, test, it, afterEach } from 'vitest'; import { vi, beforeAll, beforeEach, describe, expect, expectTypeOf, test, it, afterEach } from 'vitest';
import supertest from "supertest"; import supertest from "supertest";
import app from "../../app.js"; import app from "../../app.js";
// ignore expiration of the (self-signed) certificate import jwt from 'jsonwebtoken';
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
// set timeout
const BEFORE_ALL_TIMEOUT = 30000; // 30 sec
// set route // set route
const ROUTE = '/users/refreshjwt'; const ROUTE = '/auth';
// prepare response of each test // prepare response of each test
let response; let response;
// ############################ // ############################
// OBJECTS // OBJECTS
// ############################ // ############################
const authStoreModel = { const mockedVals = vi.hoisted(() => {
avatar: "", return {
collectionId: "_pb_users_auth_", foundUser: {
collectionName: "users", _id: '66a29da2942b3eb',
created: "2024-05-06 07:45:18.836Z", username: 'snoopy',
email: "johndoe@local.local", name: 'My User',
emailVisibility: false, email: 'user@mail.local',
id: "jr9mt8yvuri3sbd", verified: true,
name: "John Doe", role: 0,
updated: "2024-07-02 13:23:52.155Z", createdAt: '2024-07 - 25T18: 46: 58.982Z',
username: "johndoe", updatedAt: '2024-07 - 25T18: 46: 58.982Z',
verified: true __v: 0,
}; password: 'StrongPass1!',
const userLoginVerifiedResponse = { // password,
"record": authStoreModel, id: '66a29da2942b3ebcaf047f07'
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3MjA2NDk3NTQsImlkIjoianI5bXQ4eXZ1cmkzc2JkIiwidHlwZSI6ImF1dGhSZWNvcmQifQ.yFP1vlM_N2Fvpa_56INlaefSXnpwrm9ASCJuxPwf1Vk" }
}; };
});
// ############################ // ############################
// MOCKS // MOCKS
// ############################ // ############################
// import PocketBase Service // import Database Service
import * as pbService from '../../utils/pocketbase/handlePocketBase.js'; import * as dbService from '../../utils/handleDB.js';
// mock pbService // mock dbService
vi.mock('../../utils/pocketbase/handlePocketBase.js', async (importOriginal) => { vi.mock('../../utils/handleDB.js', async (importOriginal) => {
return { return {
...await importOriginal(), ...await importOriginal(),
pbVerifyAccessToken: vi.fn().mockImplementation((req, res, next) => { dbConnection: vi.fn(() => 'mocked'),
next(); findOneRecord: vi.fn(() => mockedVals.foundUser),
}), findByIdAndUpdate: vi.fn(() => { return { ...mockedVals.foundUser, refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2MOCKED' }; })
pbRefreshJWT: vi.fn().mockImplementation(() => {
return userLoginVerifiedResponse;
})
}; };
}); });
// verifyRefreshToken;
// ############################ // ############################
// TESTS // TESTS
// ############################ // ############################
describe('refresh JWT', () => { describe('refresh JWT', () => {
describe('given authtoken is valid', () => { describe('given authtoken is valid', () => {
beforeAll(async () => { beforeAll(async () => {
const refreshToken = jwt.sign({ id: mockedVals.foundUser.id }, process.env.JWT_REFRESH_KEY);
// console.log('refreshToken', refreshToken);
response = await supertest(app) response = await supertest(app)
.get(ROUTE) .get(ROUTE)
.set('Authorization', 'Bearer 123valid'); .set('Cookie', `refreshToken=${refreshToken}`);
}, BEFORE_ALL_TIMEOUT); });
it('should return a proper status code', () => { it('should return a proper status code', () => {
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should respond with a proper record and token', () => { it('should respond with a proper body', () => {
expect(response.body).toEqual(userLoginVerifiedResponse); expect(response.body).toMatchSnapshot({
accessToken: expect.any(String),
});
}); });
}); });
// ############################ // ############################
describe('given authtoken is invalid', () => { describe('given refresh token is malformed', () => {
beforeAll(async () => { beforeAll(async () => {
pbService.pbVerifyAccessToken.mockImplementation((req, res, next) => { // const refreshToken = jwt.sign({ id: mockedVals.foundUser.id }, process.env.JWT_REFRESH_KEY);
res.status(403).json({ message: 'You are not logged in.' }); // console.log('refreshToken', refreshToken);
response = await supertest(app)
.get(ROUTE)
.set('Cookie', 'refreshToken=invalid');
}); });
it('should return a proper status code', () => {
expect(response.status).toBe(400);
});
it('should respond with a proper body', () => {
expect(response.body).toMatchSnapshot();
});
});
// ############################
describe('given refresh token is invalid', () => {
beforeAll(async () => {
// const refreshToken = jwt.sign({ id: mockedVals.foundUser.id }, process.env.JWT_REFRESH_KEY);
// console.log('refreshToken', refreshToken);
response = await supertest(app) response = await supertest(app)
.get(ROUTE) .get(ROUTE)
.set('Authorization', 'Bearer 123invalid'); .set('Cookie', 'refreshToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2YTNkYTViYTEwNjUzMmNhZTEyYTYwOSIsImlhdCI6MTcyMjA5ODM3OX0.7Pq8F2zSDwuEzlCQX3vMZAw9D43N6dSViCyVPZ_s_Zs');
}, BEFORE_ALL_TIMEOUT); });
it('should return a proper status code', () => { it('should return a proper status code', () => {
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should respond with a proper record and token', () => { it('should respond with a proper body', () => {
expect(response.body.message).toEqual('You are not logged in.'); expect(response.body).toMatchSnapshot();
}); });
}); });
// ############################ // ############################
describe('given authtoken is missing', () => { describe('given refresh token is missing', () => {
beforeAll(async () => { beforeAll(async () => {
pbService.pbVerifyAccessToken.mockImplementation((req, res, next) => {
res.status(403).json({ message: 'You are not logged in.' });
});
response = await supertest(app) response = await supertest(app)
.get(ROUTE); .get(ROUTE);
}, BEFORE_ALL_TIMEOUT); });
it('should return a proper status code', () => { it('should return a proper status code', () => {
expect(response.status).toBe(403); expect(response.status).toBe(401);
}); });
it('should respond with a proper record and token', () => { it('should respond with a proper body', () => {
expect(response.body.message).toEqual('You are not logged in.'); expect(response.body).toMatchSnapshot();
}); });
}); });
}); });
\ No newline at end of file
...@@ -104,8 +104,9 @@ export const login = async (req, res, next) => { ...@@ -104,8 +104,9 @@ export const login = async (req, res, next) => {
* return new accessToken and refreshToken * return new accessToken and refreshToken
*/ */
export const renewAccessToken = async (req, res, next) => { export const renewAccessToken = async (req, res, next) => {
try {
// get token from cookie
const refreshToken = req.cookies.refreshToken; const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.status(401).json({ message: 'Refresh token is missing. Consider to re-login' }); if (!refreshToken) return res.status(401).json({ message: 'Refresh token is missing. Consider to re-login' });
// verify token // verify token
const user = await verifyRefreshToken(refreshToken); const user = await verifyRefreshToken(refreshToken);
...@@ -115,6 +116,9 @@ export const renewAccessToken = async (req, res, next) => { ...@@ -115,6 +116,9 @@ export const renewAccessToken = async (req, res, next) => {
// create & return // create & return
const accessToken = createAccessToken({ id: user._id, role: user.role }); const accessToken = createAccessToken({ id: user._id, role: user.role });
return res.json({ message: 'Access token refreshed', accessToken }); return res.json({ message: 'Access token refreshed', accessToken });
} catch (error) {
next(error);
}
}; };
......
...@@ -19,6 +19,7 @@ const generateErrorStatusCode = (error) => { ...@@ -19,6 +19,7 @@ const generateErrorStatusCode = (error) => {
switch (error.name) { switch (error.name) {
// VALIDATION ERROR // VALIDATION ERROR
case "ValidationError": case "ValidationError":
case "JsonWebTokenError":
// ZOD VALIDATION ERROR // ZOD VALIDATION ERROR
case "zodError": { case "zodError": {
return 400; return 400;
......
...@@ -93,12 +93,16 @@ export const createRefreshToken = async (payload) => { ...@@ -93,12 +93,16 @@ export const createRefreshToken = async (payload) => {
* @return {any} user from DB if exists, false if not * @return {any} user from DB if exists, false if not
*/ */
export const verifyRefreshToken = async (refreshToken) => { export const verifyRefreshToken = async (refreshToken) => {
try {
// check if in DB // check if in DB
const foundUser = await User.findOne({ refreshToken }); const foundUser = await findOneRecord(User, { refreshToken });
if (!foundUser) return false; if (!foundUser) return false;
// check if valid // check if valid
const user = jwt.verify(refreshToken, process.env.JWT_REFRESH_KEY); const user = jwt.verify(refreshToken, process.env.JWT_REFRESH_KEY);
return (user && user.id === foundUser.id ? foundUser : false); return (user && user.id === foundUser.id ? foundUser : false);
} catch (error) {
throw error;
}
}; };
/** /**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment