propro-utils 1.3.14 → 1.3.15

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "propro-utils",
3
- "version": "1.3.14",
3
+ "version": "1.3.15",
4
4
  "description": "Auth middleware for propro-auth",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -4,8 +4,90 @@ const {
4
4
  exchangeToken,
5
5
  VerifyAccount,
6
6
  } = require("./middleware/verifyToken");
7
- const {refreshTokenMiddleware} = require("./middleware");
8
- // Main middleware function
7
+ // const {refreshTokenMiddleware} = require("./middleware");
8
+
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
+ /**
79
+ * Middleware for handling authentication and authorization.
80
+ *
81
+ * @param {object} [options={}] - The options for configuring the authentication middleware.
82
+ * @param {string} [options.secret="RESTFULAPIs"] - The secret key used for authentication.
83
+ * @param {string} [options.authUrl=process.env.AUTH_URL] - The authentication URL.
84
+ * @param {string} [options.clientId=process.env.CLIENT_ID] - The client ID.
85
+ * @param {string} [options.clientSecret=process.env.CLIENT_SECRET] - The client secret.
86
+ * @param {string} [options.clientUrl=process.env.CLIENT_URL] - The client URL.
87
+ * @param {string} [options.redirectUri=process.env.REDIRECT_URI] - The redirect URI.
88
+ * @param {string} [options.appName=process.env.APP_NAME] - The application name.
89
+ * @returns {Function} - Express middleware function.
90
+ */
9
91
  function proproAuthMiddleware(options = {}) {
10
92
  const {
11
93
  secret = "RESTFULAPIs",
@@ -19,44 +101,6 @@ function proproAuthMiddleware(options = {}) {
19
101
 
20
102
  return async (req, res, next) => {
21
103
  try {
22
- let token;
23
- if (req.headers.authorization?.startsWith("Bearer ")) {
24
- token = req.headers.authorization.split(" ")[1];
25
- } else if (req.cookies && req.cookies['x-access-token']) {
26
- token = req.cookies['x-access-token'];
27
- }
28
-
29
- if (token) {
30
- console.log("verifying access token", token);
31
- const verifiedToken = await verifyJWT(token);
32
- console.log("verifiedToken", verifiedToken);
33
- if (verifiedToken) {
34
- req.account = verifiedToken;
35
- return next();
36
- } else if (req.cookies && req.cookies['x-refresh-token']) {
37
- const refreshToken = req.cookies['x-refresh-token'];
38
- if (refreshToken) {
39
- const newTokenData = await refreshTokenMiddleware(req, res, next);
40
- if (newTokenData) {
41
- res.cookie("x-refresh-token", newTokenData.tokens.refresh.token, {
42
- httpOnly: true,
43
- secure: process.env.NODE_ENV === "production",
44
- });
45
-
46
- res.cookie("x-access-token", newTokenData.tokens.access.token, {
47
- httpOnly: true,
48
- secure: process.env.NODE_ENV === "production",
49
- });
50
-
51
- req.account = verifyJWT(newTokenData.tokens.access.token, secret);
52
- }
53
- }
54
- }
55
- } else {
56
- req.account = undefined;
57
- res.status(401).send("Unauthorized: Invalid or expired token");
58
- }
59
-
60
104
  if (!["/api/auth", "/api/callback"].includes(req.path)) {
61
105
  return next();
62
106
  }
@@ -106,4 +150,4 @@ function proproAuthMiddleware(options = {}) {
106
150
  };
107
151
  }
108
152
 
109
- module.exports = proproAuthMiddleware;
153
+ module.exports = {proproAuthMiddleware, refreshTokenMiddleware};
@@ -31,20 +31,20 @@ const refreshTokenMiddleware = async (req, res, next) => {
31
31
 
32
32
  console.log("refreshToken", refreshToken);
33
33
  if (!refreshToken) {
34
- return res.status(401).json({ error: "No refresh token provided" });
34
+ res.status(401).json({ error: "No refresh token provided" });
35
35
  }
36
36
 
37
37
  console.log("isValidRefreshTokenFormat(refreshToken)", isValidRefreshTokenFormat(refreshToken));
38
38
 
39
39
  if (!isValidRefreshTokenFormat(refreshToken)) {
40
- return res.status(400).json({ error: "Invalid refresh token format" });
40
+ res.status(400).json({ error: "Invalid refresh token format" });
41
41
  }
42
42
 
43
43
  console.log("refreshTokenCache.has(refreshToken)", refreshTokenCache.has(refreshToken));
44
44
 
45
45
  if (refreshTokenCache.has(refreshToken)) {
46
46
  req.newAccessToken = refreshTokenCache.get(refreshToken);
47
- return next();
47
+ next();
48
48
  }
49
49
 
50
50
  console.log("process.env.AUTH_URL", process.env.AUTH_URL);
@@ -66,14 +66,14 @@ const refreshTokenMiddleware = async (req, res, next) => {
66
66
  if (response.data && response.data.access_token) {
67
67
  refreshTokenCache.set(refreshToken, response.data.access_token);
68
68
  req.newAccessToken = response.data.access_token;
69
- return next();
69
+ next();
70
70
  } else {
71
- return res.status(401).json({ error: "Unable to refresh token" });
71
+ res.status(401).json({ error: "Unable to refresh token" });
72
72
  }
73
73
  } catch (error) {
74
74
  const statusCode = error.response?.status || 500;
75
75
  const message = error.response?.data?.error || "Error refreshing token";
76
- return res.status(statusCode).json({ error: message });
76
+ res.status(statusCode).json({ error: message });
77
77
  }
78
78
  });
79
79
  };