propro-utils 1.3.15 → 1.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="JavaScriptLibraryMappings">
4
+ <includedPredefinedLibrary name="Node.js Core" />
5
+ </component>
6
+ </project>
@@ -0,0 +1 @@
1
+ module.exports.refreshAccessToken = require('./refresh_token');
@@ -0,0 +1,77 @@
1
+ const axios = require('axios');
2
+ const rateLimit = require('express-rate-limit');
3
+ require('dotenv').config();
4
+
5
+ // Rate limiter configuration
6
+ const refreshLimiter = rateLimit({
7
+ windowMs: 15 * 60 * 1000, // 15 minutes
8
+ max: 100, // limit each IP to 100 requests per windowMs
9
+ message: 'Too many refresh requests from this IP, please try again after 15 minutes',
10
+ });
11
+
12
+ const refreshTokenCache = new Map();
13
+
14
+ /**
15
+ * Middleware to refresh access token using refresh token.
16
+ *
17
+ * @param {Object} req - The request object.
18
+ * @param {Object} res - The response object.
19
+ * @param {Function} next - The next middleware function.
20
+ * @returns {void}
21
+ */
22
+ async function refreshTokenMiddleware(req, res, next) {
23
+ const refreshToken = req.cookies['x-refresh-token'];
24
+ if (!refreshToken) {
25
+ return res.status(401).json({ error: 'No refresh token provided' });
26
+ }
27
+
28
+ if (!isValidRefreshTokenFormat(refreshToken)) {
29
+ return res.status(400).json({ error: 'Invalid refresh token format' });
30
+ }
31
+
32
+ if (refreshTokenCache.has(refreshToken)) {
33
+ req.newAccessToken = refreshTokenCache.get(refreshToken);
34
+ return next();
35
+ }
36
+
37
+ try {
38
+ const response = await axios.post(
39
+ `${process.env.AUTH_URL}/oauth/token`,
40
+ new URLSearchParams({
41
+ grant_type: 'refresh_token',
42
+ refresh_token: refreshToken,
43
+ client_id: process.env.CLIENT_ID,
44
+ client_secret: process.env.CLIENT_SECRET,
45
+ }),
46
+ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
47
+ );
48
+
49
+ if (response.data && response.data.access_token) {
50
+ refreshTokenCache.set(refreshToken, response.data.access_token);
51
+ req.newAccessToken = response.data.access_token;
52
+ next();
53
+ } else {
54
+ res.status(401).json({ error: 'Unable to refresh token' });
55
+ }
56
+ } catch (error) {
57
+ const statusCode = error.response?.status || 500;
58
+ res.status(statusCode).json({ error: error.response?.data?.error || 'Error refreshing token' });
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Checks if the given token has a valid refresh token format.
64
+ *
65
+ * @param {string} token - The token to validate.
66
+ *
67
+ * @return {boolean} - Returns true if the token has a valid refresh token format, otherwise false.
68
+ */
69
+ function isValidRefreshTokenFormat(token) {
70
+ const jwtPattern = /^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/;
71
+ return jwtPattern.test(token);
72
+ }
73
+
74
+ module.exports = {
75
+ refreshTokenMiddleware,
76
+ refreshLimiter
77
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "propro-utils",
3
- "version": "1.3.15",
3
+ "version": "1.3.16",
4
4
  "description": "Auth middleware for propro-auth",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,80 +1,8 @@
1
1
  require("dotenv").config();
2
2
  const {
3
- verifyJWT,
4
3
  exchangeToken,
5
- VerifyAccount,
6
4
  } = require("./middleware/verifyToken");
7
- // const {refreshTokenMiddleware} = require("./middleware");
8
5
 
9
- const refreshLimiter = rateLimit({
10
- windowMs: 15 * 60 * 1000, // 15 minutes
11
- max: 100, // Limit each IP to 100 requests per 15 minutes
12
- message: "Too many refresh requests from this IP, please try again after 15 minutes",
13
- });
14
-
15
- const refreshTokenCache = new Map();
16
-
17
- /**
18
- * Middleware to refresh access token using refresh token.
19
- *
20
- * @param {Object} req - The request object.
21
- * @param {Object} res - The response object.
22
- * @param {Function} next - The next middleware function.
23
- * @returns {void}
24
- */
25
- async function refreshTokenMiddleware(req, res, next) {
26
- refreshLimiter(req, res, async () => {
27
- const refreshToken = req.cookies['x-refresh-token'];
28
- if (!refreshToken) {
29
- return res.status(401).json({ error: "No refresh token provided" });
30
- }
31
-
32
- if (!isValidRefreshTokenFormat(refreshToken)) {
33
- return res.status(400).json({ error: "Invalid refresh token format" });
34
- }
35
-
36
- if (refreshTokenCache.has(refreshToken)) {
37
- req.newAccessToken = refreshTokenCache.get(refreshToken);
38
- return next();
39
- }
40
-
41
- try {
42
- const response = await axios.post(
43
- `${process.env.AUTH_URL}/oauth/token`,
44
- new URLSearchParams({
45
- grant_type: "refresh_token",
46
- refresh_token: refreshToken,
47
- client_id: process.env.CLIENT_ID,
48
- client_secret: process.env.CLIENT_SECRET,
49
- }),
50
- { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
51
- );
52
-
53
- if (response.data && response.data.access_token) {
54
- refreshTokenCache.set(refreshToken, response.data.access_token);
55
- req.newAccessToken = response.data.access_token;
56
- next();
57
- } else {
58
- res.status(401).json({ error: "Unable to refresh token" });
59
- }
60
- } catch (error) {
61
- const statusCode = error.response?.status || 500;
62
- res.status(statusCode).json({ error: error.response?.data?.error || "Error refreshing token" });
63
- }
64
- });
65
- }
66
-
67
- /**
68
- * Checks if the given token has a valid refresh token format.
69
- *
70
- * @param {string} token - The token to validate.
71
- *
72
- * @return {boolean} - Returns true if the token has a valid refresh token format, otherwise false.
73
- */
74
- function isValidRefreshTokenFormat(token) {
75
- const jwtPattern = /^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/;
76
- return jwtPattern.test(token);
77
- }
78
6
  /**
79
7
  * Middleware for handling authentication and authorization.
80
8
  *
@@ -150,4 +78,4 @@ function proproAuthMiddleware(options = {}) {
150
78
  };
151
79
  }
152
80
 
153
- module.exports = {proproAuthMiddleware, refreshTokenMiddleware};
81
+ module.exports = proproAuthMiddleware;
@@ -1,3 +1,2 @@
1
- module.exports.refreshTokenMiddleware = require("./refreshToken");
2
1
  module.exports.verifyTokenMiddleware = require("./verifyToken");
3
2
  module.exports.validateEnv = require("./validateEnv");
@@ -1,203 +0,0 @@
1
- describe('Test Middleware', () => {
2
- // Middleware correctly verifies JWT token and sets user in request object
3
- const jwt = require('jsonwebtoken');
4
- const middleware = require('../middleware');
5
-
6
- jest.mock('jsonwebtoken');
7
-
8
- describe('Set user if the jwt is valid', () => {
9
- it('should set user in request object when JWT token is valid', () => {
10
- const req = {
11
- headers: {
12
- authorization: 'JWT validToken',
13
- },
14
- };
15
- const res = {};
16
- const next = jest.fn();
17
- const decode = { username: 'testUser' };
18
- jwt.verify.mockImplementation((token, secret, callback) => {
19
- callback(null, decode);
20
- });
21
-
22
- middleware(req, res, next);
23
-
24
- expect(req.user).toEqual(decode);
25
- expect(next).toHaveBeenCalled();
26
- });
27
- });
28
-
29
- // GET request to '/test-refresh-token' returns JSON with message and access token
30
- const request = require('supertest');
31
- const app = require('..');
32
-
33
- describe('return a message and an access token', () => {
34
- it('should return JSON with message and access token', async () => {
35
- const response = await request(app)
36
- .get('/test-refresh-token')
37
- .set('Authorization', 'JWT validToken');
38
-
39
- expect(response.status).toBe(200);
40
- expect(response.body.message).toBe('Access granted with a new token');
41
- expect(response.body.accessToken).toBeDefined();
42
- });
43
- });
44
-
45
- // GET request to '/auth' redirects to authorization server URL with correct parameters
46
- const request = require('supertest');
47
- const app = require('..');
48
-
49
- describe('redirect with correct parameter', () => {
50
- it('should redirect to authorization server URL with correct parameters', async () => {
51
- process.env.AUTH_URL = 'http://auth-server.com';
52
- process.env.CLIENT_ID = 'testClientId';
53
- process.env.REDIRECT_URI = 'http://client.com/callback';
54
-
55
- const response = await request(app).get('/auth');
56
-
57
- expect(response.status).toBe(302);
58
- expect(response.header.location).toBe(
59
- 'http://auth-server.com/oauth/authorize?response_type=code&client_id=testClientId&redirect_uri=http%3A%2F%2Fclient.com%2Fcallback',
60
- );
61
- });
62
- });
63
-
64
- // Authorization header is missing or invalid
65
- const middleware = require('../middleware');
66
-
67
- describe('Set user to undefined on missing header', () => {
68
- it('should set user to undefined when authorization header is missing or invalid', () => {
69
- const req = {
70
- headers: {},
71
- };
72
- const res = {};
73
- const next = jest.fn();
74
-
75
- middleware(req, res, next);
76
-
77
- expect(req.user).toBeUndefined();
78
- expect(next).toHaveBeenCalled();
79
- });
80
- });
81
-
82
- // No authorization code received in '/callback' endpoint
83
- const request = require('supertest');
84
- const app = require('..');
85
-
86
- describe('Error when excchange code is received', () => {
87
- it('should return 400 when no authorization code is received', async () => {
88
- const response = await request(app).get('/callback');
89
-
90
- expect(response.status).toBe(400);
91
- expect(response.text).toBe('No code received');
92
- });
93
- });
94
-
95
- // Error occurs during token exchange in '/callback' endpoint
96
- const request = require('supertest');
97
- const axios = require('axios');
98
- const app = require('..');
99
-
100
- jest.mock('axios');
101
-
102
- describe('Error during token exchange', () => {
103
- it('should return 500 when error occurs during token exchange', async () => {
104
- const req = {
105
- query: {
106
- code: 'validCode',
107
- },
108
- };
109
- const res = {};
110
- axios.post.mockRejectedValue(new Error('Test error'));
111
-
112
- const response = await request(app).get('/callback').query(req.query);
113
-
114
- expect(response.status).toBe(500);
115
- expect(response.text).toBe('Internal Server Error');
116
- });
117
- });
118
-
119
- // GET request to '/callback' exchanges authorization code for access token and sets cookie
120
- it('should exchange authorization code for access token and set cookie', async () => {
121
- const req = {
122
- query: {
123
- code: 'authorizationCode',
124
- },
125
- };
126
- const res = {
127
- status: jest.fn().mockReturnThis(),
128
- send: jest.fn(),
129
- redirect: jest.fn(),
130
- cookie: jest.fn(),
131
- };
132
- const tokenResponse = {
133
- data: {
134
- access_token: 'accessToken',
135
- refresh_token: 'refreshToken',
136
- },
137
- };
138
- axios.post.mockResolvedValue(tokenResponse);
139
-
140
- await callback(req, res);
141
-
142
- expect(axios.post).toHaveBeenCalledWith(
143
- `${process.env.AUTH_URL}/oauth/token`,
144
- qs.stringify({
145
- grant_type: 'authorization_code',
146
- code: req.query.code,
147
- redirect_uri: process.env.REDIRECT_URI,
148
- client_id: process.env.CLIENT_ID,
149
- client_secret: process.env.CLIENT_SECRET,
150
- }),
151
- {
152
- headers: {
153
- 'Content-Type': 'application/json',
154
- },
155
- },
156
- );
157
- expect(res.cookie).toHaveBeenCalledWith(
158
- 'token',
159
- tokenResponse.data.access_token,
160
- {
161
- httpOnly: true,
162
- secure: process.env.NODE_ENV === 'production',
163
- },
164
- );
165
- expect(res.redirect).toHaveBeenCalledWith(
166
- `${process.env.CLIENT_URL}/?access_token=${tokenResponse.data.access_token}&refresh_token=${tokenResponse.data.refresh_token}`,
167
- );
168
- });
169
-
170
- // Unauthorized user receives 401 status code
171
- it('should return 401 status code for unauthorized user', () => {
172
- const req = {};
173
- const res = {
174
- status: jest.fn().mockReturnThis(),
175
- send: jest.fn(),
176
- };
177
-
178
- middleware(req, res);
179
-
180
- expect(res.status).toHaveBeenCalledWith(401);
181
- expect(res.send).toHaveBeenCalledWith('Unauthorized');
182
- });
183
-
184
- // Invalid redirect URI returns error in '/auth' endpoint
185
- it('should return error for invalid redirect URI', () => {
186
- const req = {};
187
- const res = {
188
- status: jest.fn().mockReturnThis(),
189
- send: jest.fn(),
190
- };
191
- const authServerUrl = `${process.env.AUTH_URL}/oauth/authorize`;
192
- const clientId = process.env.CLIENT_ID;
193
- const redirectUri = process.env.REDIRECT_URI;
194
-
195
- auth(req, res);
196
-
197
- expect(res.redirect).toHaveBeenCalledWith(
198
- `${authServerUrl}?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(
199
- redirectUri,
200
- )}`,
201
- );
202
- });
203
- });
@@ -1,92 +0,0 @@
1
- const axios = require("axios");
2
- const rateLimit = require("express-rate-limit");
3
- const refreshTokenCache = new Map();
4
-
5
- /**
6
- * Rate limiter middleware for refresh requests.
7
- *
8
- * @type {import('express-rate-limit').RateLimit}
9
- */
10
- const refreshLimiter = rateLimit({
11
- windowMs: 15 * 60 * 1000, // 15 minutes
12
- max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
13
- message:
14
- "Too many refresh requests from this IP, please try again after 15 minutes",
15
- });
16
-
17
- /**
18
- * Middleware function to refresh access token using refresh token.
19
- * @async
20
- * @function
21
- * @param {Object} req - Express request object.
22
- * @param {Object} res - Express response object.
23
- * @param {Function} next - Express next middleware function.
24
- * @returns {Promise<void>} - Promise object that represents the completion of the middleware function.
25
- */
26
- const refreshTokenMiddleware = async (req, res, next) => {
27
- console.log("refreshTokenMiddleware again");
28
- // Apply rate limiting
29
- refreshLimiter(req, res, async () => {
30
- const refreshToken = req.headers["x-refresh-token"];
31
-
32
- console.log("refreshToken", refreshToken);
33
- if (!refreshToken) {
34
- res.status(401).json({ error: "No refresh token provided" });
35
- }
36
-
37
- console.log("isValidRefreshTokenFormat(refreshToken)", isValidRefreshTokenFormat(refreshToken));
38
-
39
- if (!isValidRefreshTokenFormat(refreshToken)) {
40
- res.status(400).json({ error: "Invalid refresh token format" });
41
- }
42
-
43
- console.log("refreshTokenCache.has(refreshToken)", refreshTokenCache.has(refreshToken));
44
-
45
- if (refreshTokenCache.has(refreshToken)) {
46
- req.newAccessToken = refreshTokenCache.get(refreshToken);
47
- next();
48
- }
49
-
50
- console.log("process.env.AUTH_URL", process.env.AUTH_URL);
51
-
52
- try {
53
- const response = await axios.post(
54
- `${process.env.AUTH_URL}/oauth/token`,
55
- URLSearchParams({
56
- grant_type: "refresh_token",
57
- refresh_token: refreshToken,
58
- client_id: process.env.CLIENT_ID,
59
- client_secret: process.env.CLIENT_SECRET,
60
- }),
61
- { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
62
- );
63
-
64
- console.log("response", response);
65
-
66
- if (response.data && response.data.access_token) {
67
- refreshTokenCache.set(refreshToken, response.data.access_token);
68
- req.newAccessToken = response.data.access_token;
69
- next();
70
- } else {
71
- res.status(401).json({ error: "Unable to refresh token" });
72
- }
73
- } catch (error) {
74
- const statusCode = error.response?.status || 500;
75
- const message = error.response?.data?.error || "Error refreshing token";
76
- res.status(statusCode).json({ error: message });
77
- }
78
- });
79
- };
80
-
81
- /**
82
- * Checks if the given token is in a valid JWT format.
83
- *
84
- * @param {string} token - The token to validate.
85
- * @returns {boolean} - True if the token is in a valid format, false otherwise.
86
- */
87
- function isValidRefreshTokenFormat(token) {
88
- const jwtPattern = /^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/;
89
- return jwtPattern.test(token);
90
- }
91
-
92
- module.exports = refreshTokenMiddleware;
@@ -1,121 +0,0 @@
1
- const rateLimit = require("express-rate-limit");
2
- const refreshToken = require("./refreshToken");
3
-
4
- jest.mock("express-rate-limit");
5
-
6
- describe("refreshLimiter", () => {
7
- it("should be a rate limiter middleware", () => {
8
- expect(rateLimit).toHaveBeenCalledWith({
9
- windowMs: 15 * 60 * 1000,
10
- max: 100,
11
- message:
12
- "Too many refresh requests from this IP, please try again after 15 minutes",
13
- });
14
- });
15
-
16
- it("should be exported as a middleware function", () => {
17
- expect(typeof refreshToken).toBe("function");
18
- });
19
-
20
- it("should use the rate limiter middleware", () => {
21
- const mockUse = jest.fn();
22
- const app = { use: mockUse };
23
-
24
- refreshToken(app);
25
-
26
- expect(mockUse).toHaveBeenCalledWith(rateLimit());
27
- });
28
- });
29
-
30
- describe("refreshTokenMiddleware", () => {
31
- const mockRequest = (refreshToken) => ({
32
- headers: { "x-refresh-token": refreshToken },
33
- });
34
- const mockResponse = () => {
35
- const res = {};
36
- res.status = jest.fn().mockReturnValue(res);
37
- res.json = jest.fn().mockReturnValue(res);
38
- return res;
39
- };
40
- const mockNext = jest.fn();
41
-
42
- beforeEach(() => {
43
- jest.clearAllMocks();
44
- });
45
-
46
- it("should return 401 if no refresh token is provided", async () => {
47
- const req = mockRequest();
48
- const res = mockResponse();
49
-
50
- await refreshTokenMiddleware(req, res, mockNext);
51
-
52
- expect(res.status).toHaveBeenCalledWith(401);
53
- expect(res.json).toHaveBeenCalledWith({
54
- error: "No refresh token provided",
55
- });
56
- expect(mockNext).not.toHaveBeenCalled();
57
- });
58
-
59
- it("should return 400 if refresh token has invalid format", async () => {
60
- const req = mockRequest("invalid-token");
61
- const res = mockResponse();
62
-
63
- await refreshTokenMiddleware(req, res, mockNext);
64
-
65
- expect(res.status).toHaveBeenCalledWith(400);
66
- expect(res.json).toHaveBeenCalledWith({
67
- error: "Invalid refresh token format",
68
- });
69
- expect(mockNext).not.toHaveBeenCalled();
70
- });
71
-
72
- it("should return 401 if unable to refresh token", async () => {
73
- const req = mockRequest("valid-token");
74
- const res = mockResponse();
75
- axios.post.mockRejectedValue({ response: { status: 401 } });
76
-
77
- await refreshTokenMiddleware(req, res, mockNext);
78
-
79
- expect(res.status).toHaveBeenCalledWith(401);
80
- expect(res.json).toHaveBeenCalledWith({ error: "Unable to refresh token" });
81
- expect(mockNext).not.toHaveBeenCalled();
82
- });
83
-
84
- it("should return 500 if an error occurs while refreshing token", async () => {
85
- const req = mockRequest("valid-token");
86
- const res = mockResponse();
87
- axios.post.mockRejectedValue(new Error("Some error"));
88
-
89
- await refreshTokenMiddleware(req, res, mockNext);
90
-
91
- expect(res.status).toHaveBeenCalledWith(500);
92
- expect(res.json).toHaveBeenCalledWith({ error: "Error refreshing token" });
93
- expect(mockNext).not.toHaveBeenCalled();
94
- });
95
-
96
- it("should set new access token and call next if refresh token is valid", async () => {
97
- const req = mockRequest("valid-token");
98
- const res = mockResponse();
99
- axios.post.mockResolvedValue({
100
- data: { access_token: "new-access-token" },
101
- });
102
-
103
- await refreshTokenMiddleware(req, res, mockNext);
104
-
105
- expect(refreshTokenCache.has("valid-token")).toBe(true);
106
- expect(refreshTokenCache.get("valid-token")).toBe("new-access-token");
107
- expect(req.newAccessToken).toBe("new-access-token");
108
- expect(mockNext).toHaveBeenCalled();
109
- });
110
-
111
- it("should use cached access token and call next if refresh token is already cached", async () => {
112
- refreshTokenCache.set("valid-token", "cached-access-token");
113
- const req = mockRequest("valid-token");
114
- const res = mockResponse();
115
-
116
- await refreshTokenMiddleware(req, res, mockNext);
117
-
118
- expect(req.newAccessToken).toBe("cached-access-token");
119
- expect(mockNext).toHaveBeenCalled();
120
- });
121
- });
@@ -1,171 +0,0 @@
1
- describe("isValid", () => {
2
- it("should return true when all required permissions are present", () => {
3
- const decodedToken = { permissions: ["user", "manageUsers"] };
4
- const requiredPermissions = ["user", "manageUsers"];
5
-
6
- const result = isValid(decodedToken, requiredPermissions);
7
-
8
- expect(result).toBe(true);
9
- });
10
-
11
- it("should return false when some required permissions are missing", () => {
12
- const decodedToken = { permissions: ["user"] };
13
- const requiredPermissions = ["user", "manageUsers"];
14
-
15
- const result = isValid(decodedToken, requiredPermissions);
16
-
17
- expect(result).toBe(false);
18
- });
19
-
20
- it("should return false when all required permissions are missing", () => {
21
- const decodedToken = { permissions: [] };
22
- const requiredPermissions = ["user", "manageUsers"];
23
-
24
- const result = isValid(decodedToken, requiredPermissions);
25
-
26
- expect(result).toBe(false);
27
- });
28
-
29
- it("should return true when no permissions are required", () => {
30
- const decodedToken = { permissions: ["user", "manageUsers"] };
31
- const requiredPermissions = [];
32
-
33
- const result = isValid(decodedToken, requiredPermissions);
34
-
35
- expect(result).toBe(true);
36
- });
37
- });
38
-
39
- describe("VerifyAccount", () => {
40
- // Verify that a valid access token is present in the request header and proceed with the verification process.
41
- it("should return a 401 error response when access token is not present in the request header", async () => {
42
- const req = { headers: {} };
43
- const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
44
- const next = jest.fn();
45
-
46
- await VerifyAccount([])(req, res, next);
47
-
48
- expect(res.status).toHaveBeenCalledWith(401);
49
- expect(res.json).toHaveBeenCalledWith({
50
- error: "Access token is required",
51
- });
52
- expect(next).not.toHaveBeenCalled();
53
- });
54
-
55
- // Verify that the account has all the required permissions and attach account info to the request object.
56
- it("should return a 403 error response when the account does not have all the required permissions", async () => {
57
- const requiredPermissions = ["user", "manageUsers"];
58
- const account = { id: "123", permissions: ["user"] };
59
- const req = { headers: { authorization: "Bearer token" }, account };
60
- const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
61
- const next = jest.fn();
62
-
63
- await VerifyAccount(requiredPermissions)(req, res, next);
64
-
65
- expect(res.status).toHaveBeenCalledWith(403);
66
- expect(res.json).toHaveBeenCalledWith({
67
- error: "Insufficient permissions",
68
- });
69
- expect(next).not.toHaveBeenCalled();
70
- });
71
-
72
- // Verify that the function calls the next middleware function after successful verification.
73
- it("should call the next middleware function after successful verification", async () => {
74
- const requiredPermissions = ["user", "manageUsers"];
75
- const account = { id: "123", permissions: ["user", "manageUsers"] };
76
- const req = { headers: { authorization: "Bearer token" }, account };
77
- const res = { status: jest.fn(), json: jest.fn() };
78
- const next = jest.fn();
79
-
80
- await VerifyAccount(requiredPermissions)(req, res, next);
81
-
82
- expect(res.status).not.toHaveBeenCalled();
83
- expect(res.json).not.toHaveBeenCalled();
84
- expect(next).toHaveBeenCalled();
85
- });
86
-
87
- // Verify that the function returns a 500 error response when an unexpected error occurs during verification.
88
- it("should return a 500 error response when an unexpected error occurs during verification", async () => {
89
- const requiredPermissions = ["user", "manageUsers"];
90
- const req = { headers: { authorization: "Bearer token" } };
91
- const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
92
- const next = jest.fn();
93
-
94
- await VerifyAccount(requiredPermissions)(req, res, next);
95
-
96
- expect(res.status).toHaveBeenCalledWith(500);
97
- expect(res.json).toHaveBeenCalledWith({ error: "Internal Server Error" });
98
- expect(next).not.toHaveBeenCalled();
99
- });
100
-
101
- // Verify that the function handles errors returned by the external API and returns a 401 error response when the access token is invalid or expired.
102
- it("should return a 401 error response when the access token is invalid or expired", async () => {
103
- const requiredPermissions = ["user", "manageUsers"];
104
- const req = { headers: { authorization: "Bearer token" } };
105
- const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
106
- const next = jest.fn();
107
-
108
- axios.get.mockRejectedValueOnce({ response: { status: 401 } });
109
-
110
- await VerifyAccount(requiredPermissions)(req, res, next);
111
-
112
- expect(res.status).toHaveBeenCalledWith(401);
113
- expect(res.json).toHaveBeenCalledWith({
114
- error: "Invalid or expired access token",
115
- });
116
- expect(next).not.toHaveBeenCalled();
117
- });
118
-
119
- // Verify that the function handles errors returned by the external API and returns a 500 error response when an unexpected error occurs during API call.
120
- it("should return a 500 error response when an unexpected error occurs during API call", async () => {
121
- const requiredPermissions = ["user", "manageUsers"];
122
- const req = { headers: { authorization: "Bearer token" } };
123
- const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
124
- const next = jest.fn();
125
-
126
- axios.get.mockRejectedValueOnce(new Error("API error"));
127
-
128
- await VerifyAccount(requiredPermissions)(req, res, next);
129
-
130
- expect(res.status).toHaveBeenCalledWith(500);
131
- expect(res.json).toHaveBeenCalledWith({ error: "Internal Server Error" });
132
- expect(next).not.toHaveBeenCalled();
133
- });
134
-
135
- // Verify that the function retrieves the user from cache when the token is already cached.
136
- it("should retrieve the user from cache when the token is already cached", async () => {
137
- const requiredPermissions = ["user", "manageUsers"];
138
- const account = { id: "123", permissions: ["user", "manageUsers"] };
139
- const accessToken = "token";
140
- const decoded = { ...account, accessToken };
141
- const req = { headers: { authorization: `Bearer ${accessToken}` } };
142
- const res = { status: jest.fn(), json: jest.fn() };
143
- const next = jest.fn();
144
-
145
- tokenCache.set(accessToken, decoded);
146
-
147
- await VerifyAccount(requiredPermissions)(req, res, next);
148
-
149
- expect(req.user).toEqual(decoded);
150
- expect(next).toHaveBeenCalled();
151
- });
152
-
153
- // Verify that the function retrieves the user from the external API when the token is not cached.
154
- it("should retrieve the user from the external API when the token is not cached", async () => {
155
- const requiredPermissions = ["user", "manageUsers"];
156
- const account = { id: "123", permissions: ["user", "manageUsers"] };
157
- const accessToken = "token";
158
- const decoded = { ...account, accessToken };
159
- const req = { headers: { authorization: `Bearer ${accessToken}` } };
160
- const res = { status: jest.fn(), json: jest.fn() };
161
- const next = jest.fn();
162
-
163
- axios.get.mockResolvedValueOnce({ data: decoded });
164
-
165
- await VerifyAccount(requiredPermissions)(req, res, next);
166
-
167
- expect(req.user).toEqual(decoded);
168
- expect(tokenCache.get(accessToken)).toEqual(decoded);
169
- expect(next).toHaveBeenCalled();
170
- });
171
- });