propro-utils 1.6.3 → 1.6.5

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.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "Auth middleware for propro-auth",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -46,6 +46,7 @@
46
46
  "jest-mock-axios": "^4.7.3",
47
47
  "jsonwebtoken": "^9.0.2",
48
48
  "multer": "^1.4.5-lts.1",
49
+ "neverthrow": "^8.2.0",
49
50
  "nodemailer": "^6.9.7",
50
51
  "nodemailer-mailgun-transport": "^2.1.5",
51
52
  "querystring": "^0.2.1",
@@ -3,19 +3,19 @@
3
3
  * This module provides functions for setting and clearing authentication cookies.
4
4
  */
5
5
 
6
- const { URL } = require('url');
6
+ const { URL } = require("url");
7
7
 
8
8
  /**
9
9
  * Safely stringify an object, handling circular references
10
10
  * @param {Object} obj - The object to stringify
11
11
  * @return {string} A JSON string representation of the object
12
12
  */
13
- const safeStringify = obj => {
13
+ const safeStringify = (obj) => {
14
14
  const seen = new WeakSet();
15
15
  return JSON.stringify(obj, (key, value) => {
16
- if (typeof value === 'object' && value !== null) {
16
+ if (typeof value === "object" && value !== null) {
17
17
  if (seen.has(value)) {
18
- return '[Circular]';
18
+ return "[Circular]";
19
19
  }
20
20
  seen.add(value);
21
21
  }
@@ -28,7 +28,7 @@ const safeStringify = obj => {
28
28
  * @param {Object} user - The user object to sanitize
29
29
  * @return {Object} A sanitized version of the user object
30
30
  */
31
- const sanitizeUser = user => {
31
+ const sanitizeUser = (user) => {
32
32
  const sanitized = { ...user };
33
33
 
34
34
  delete sanitized.password;
@@ -61,10 +61,10 @@ const sanitizeUser = user => {
61
61
  * @param {Object} details - Cookie details
62
62
  * @returns {Promise} Promise that resolves when cookie is set
63
63
  */
64
- const setChromeExtensionCookie = details => {
64
+ const setChromeExtensionCookie = (details) => {
65
65
  return new Promise((resolve, reject) => {
66
66
  try {
67
- chrome.cookies.set(details, cookie => {
67
+ chrome.cookies.set(details, (cookie) => {
68
68
  if (chrome.runtime.lastError) {
69
69
  reject(chrome.runtime.lastError);
70
70
  } else {
@@ -83,13 +83,13 @@ const setChromeExtensionCookie = details => {
83
83
  */
84
84
  const setAuthCookies = async (res, tokens, account, user, appUrl) => {
85
85
  if (!tokens?.refresh?.token || !tokens?.access?.token) {
86
- throw new Error('Invalid tokens object');
86
+ throw new Error("Invalid tokens object");
87
87
  }
88
88
  if (!account) {
89
- throw new Error('Invalid account object');
89
+ throw new Error("Invalid account object");
90
90
  }
91
91
  if (!user) {
92
- throw new Error('Invalid user object');
92
+ throw new Error("Invalid user object");
93
93
  }
94
94
 
95
95
  const currentDateTime = new Date();
@@ -101,35 +101,49 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
101
101
  // Domain configuration
102
102
  let domain;
103
103
  try {
104
- domain = appUrl ? new URL(appUrl).hostname : undefined;
105
- if (domain?.includes('mapmap.app')) {
106
- domain = '.mapmap.app';
104
+ // Handle URLs that don't include the protocol
105
+ let processedAppUrl = appUrl;
106
+ if (
107
+ appUrl &&
108
+ !appUrl.startsWith("http://") &&
109
+ !appUrl.startsWith("https://")
110
+ ) {
111
+ processedAppUrl = `https://${appUrl}`;
107
112
  }
108
- if (domain?.includes('localhost')) {
113
+
114
+ domain = processedAppUrl ? new URL(processedAppUrl).hostname : undefined;
115
+ if (domain?.includes("mapmap.app")) {
116
+ domain = ".mapmap.app";
117
+ }
118
+ if (domain?.includes("localhost")) {
109
119
  domain = undefined;
110
120
  }
111
- if (domain?.includes('propro.so')) {
112
- domain = 'propro.so';
121
+ if (domain?.includes("propro.so")) {
122
+ domain = "propro.so";
113
123
  }
114
124
  } catch (error) {
115
- console.error('Invalid appUrl:', { error, appUrl });
125
+ console.error("Invalid appUrl:", { error, appUrl });
116
126
  domain = undefined;
117
127
  }
118
128
 
129
+ // Determine if we're in a local development environment
130
+ const isLocalhost =
131
+ !domain || domain === "localhost" || domain.includes("localhost");
132
+
119
133
  const commonAttributes = {
120
- secure: true,
121
- sameSite: 'None',
134
+ secure: !isLocalhost, // Only require secure for non-localhost environments
135
+ sameSite: isLocalhost ? "Lax" : "None", // Use Lax for localhost, None for production
122
136
  domain,
123
- path: '/',
137
+ path: "/",
124
138
  };
125
139
 
126
140
  const httpOnlyCookies = {
127
- 'x-refresh-token': {
141
+ "x-refresh-token": {
128
142
  value: tokens.refresh.token,
129
143
  maxAge: refreshMaxAge,
130
144
  httpOnly: true,
131
145
  },
132
- 'x-access-token': {
146
+ "x-access-token": {
133
147
  value: tokens.access.token,
134
148
  maxAge: accessMaxAge,
135
149
  httpOnly: true,
@@ -150,7 +164,7 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
150
164
  maxAge: refreshMaxAge,
151
165
  },
152
166
  has_account_token: {
153
- value: JSON.stringify({ value: 'true', expires: accessMaxAge }),
167
+ value: JSON.stringify({ value: "true", expires: accessMaxAge }),
154
168
  maxAge: accessMaxAge,
155
169
  },
156
170
  };
@@ -170,21 +184,21 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
170
184
  ...regularCookies,
171
185
  }).map(([name, config]) => {
172
186
  return setChromeExtensionCookie({
173
- url: `https://${domain || 'propro.so'}`,
187
+ url: `https://${domain || "propro.so"}`,
174
188
  name,
175
189
  value: config.value,
176
190
  secure: true,
177
191
  httpOnly: !!config.httpOnly,
178
- sameSite: 'no_restriction',
179
- path: '/',
192
+ sameSite: "no_restriction",
193
+ path: "/",
180
194
  expirationDate: Math.floor((Date.now() + config.maxAge) / 1000),
181
- domain: domain?.startsWith('.') ? domain : `.${domain || 'propro.so'}`,
195
+ domain: domain?.startsWith(".") ? domain : `.${domain || "propro.so"}`,
182
196
  });
183
197
  });
184
198
 
185
199
  await Promise.allSettled(extensionCookiePromises);
186
200
 
187
- console.log('Auth cookies set successfully', {
201
+ console.log("Auth cookies set successfully", {
188
202
  domain,
189
203
  sameSite: commonAttributes.sameSite,
190
204
  cookieNames: [
@@ -193,11 +207,11 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
193
207
  ],
194
208
  });
195
209
  } catch (error) {
196
- console.error('Error setting cookies:', {
210
+ console.error("Error setting cookies:", {
197
211
  error: error.message,
198
212
  stack: error.stack,
199
213
  });
200
- throw new Error('Failed to set authentication cookies');
214
+ throw new Error("Failed to set authentication cookies");
201
215
  }
202
216
  };
203
217
 
@@ -207,45 +221,59 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
207
221
  const clearAuthCookies = async (res, appUrl) => {
208
222
  let domain;
209
223
  try {
210
- domain = appUrl ? new URL(appUrl).hostname : undefined;
211
- if (domain?.includes('mapmap.app')) {
212
- domain = '.mapmap.app';
224
+ // Handle URLs that don't include the protocol
225
+ let processedAppUrl = appUrl;
226
+ if (
227
+ appUrl &&
228
+ !appUrl.startsWith("http://") &&
229
+ !appUrl.startsWith("https://")
230
+ ) {
231
+ processedAppUrl = `https://${appUrl}`;
213
232
  }
214
- if (domain?.includes('localhost')) {
233
+
234
+ domain = processedAppUrl ? new URL(processedAppUrl).hostname : undefined;
235
+ if (domain?.includes("mapmap.app")) {
236
+ domain = ".mapmap.app";
237
+ }
238
+ if (domain?.includes("localhost")) {
215
239
  domain = undefined;
216
240
  }
217
241
  } catch (error) {
218
- console.error('Invalid appUrl:', error);
242
+ console.error("Invalid appUrl:", error);
219
243
  domain = undefined;
220
244
  }
221
245
 
246
+ // Determine if we're in a local development environment
247
+ const isLocalhost =
248
+ !domain || domain === "localhost" || domain.includes("localhost");
249
+
222
250
  const commonAttributes = {
223
- secure: true,
224
- sameSite: 'None',
251
+ secure: !isLocalhost, // Only require secure for non-localhost environments
252
+ sameSite: isLocalhost ? "Lax" : "None", // Use Lax for localhost, None for production
225
253
  domain,
226
- path: '/',
254
+ path: "/",
227
255
  };
228
256
 
229
257
  const cookieNames = [
230
- 'x-refresh-token',
231
- 'x-access-token',
232
- 'user',
233
- 'account',
234
- 'has_account_token',
258
+ "x-refresh-token",
259
+ "x-access-token",
260
+ "user",
261
+ "account",
262
+ "has_account_token",
235
263
  ];
236
264
 
237
265
  // Clear web cookies
238
- cookieNames.forEach(cookieName => {
266
+ cookieNames.forEach((cookieName) => {
239
267
  res.clearCookie(cookieName, commonAttributes);
240
268
  });
241
269
 
242
270
  try {
243
271
  const extensionClearPromises = cookieNames.map(
244
- name =>
245
- new Promise(resolve => {
272
+ (name) =>
273
+ new Promise((resolve) => {
246
274
  chrome.cookies.remove(
247
275
  {
248
- url: `https://${domain || 'mapmap.app'}`,
276
+ url: `https://${domain || "mapmap.app"}`,
249
277
  name,
250
278
  },
251
279
  resolve
@@ -258,7 +286,7 @@ const clearAuthCookies = async (res, appUrl) => {
258
286
  // Not in extension context, ignore
259
287
  }
260
288
 
261
- console.log('Auth cookies cleared successfully', {
289
+ console.log("Auth cookies cleared successfully", {
262
290
  domain,
263
291
  cookieNames,
264
292
  sameSite: commonAttributes.sameSite,