mailsentry-auth 0.1.0
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/README.md +36 -0
- package/ZUSTAND_MIGRATION.md +348 -0
- package/dist/index.d.mts +1492 -0
- package/dist/index.d.ts +1492 -0
- package/dist/index.js +2996 -0
- package/dist/index.mjs +2996 -0
- package/dist/middleware.mjs +803 -0
- package/dist/utils/cookie-utils.js +225 -0
- package/dist/utils/cookie-utils.mjs +225 -0
- package/package.json +103 -0
- package/src/app/actions/auth-actions.ts +22 -0
- package/src/app/actions/index.ts +3 -0
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
|
|
21
|
+
// src/middlewares/handlers/base-middleware-handler.ts
|
|
22
|
+
import { NextResponse } from "next/server";
|
|
23
|
+
|
|
24
|
+
// src/config/middleware.ts
|
|
25
|
+
var MiddlewareConfig = class {
|
|
26
|
+
};
|
|
27
|
+
// Protected routes
|
|
28
|
+
MiddlewareConfig.PROTECTED_ROUTES = {
|
|
29
|
+
DASHBOARD: "/"
|
|
30
|
+
};
|
|
31
|
+
// Auth routes
|
|
32
|
+
MiddlewareConfig.AUTH_ROUTES = {
|
|
33
|
+
LOGIN: "/login"
|
|
34
|
+
};
|
|
35
|
+
// HTTP methods to process
|
|
36
|
+
MiddlewareConfig.ALLOWED_METHODS = ["GET", "HEAD"];
|
|
37
|
+
// Query parameters
|
|
38
|
+
MiddlewareConfig.QUERY_PARAMS = {
|
|
39
|
+
LOGIN_REQUIRED: "sign_in_required",
|
|
40
|
+
AUTH_CHECKED: "auth_checked"
|
|
41
|
+
};
|
|
42
|
+
// Query parameter values
|
|
43
|
+
MiddlewareConfig.QUERY_VALUES = {
|
|
44
|
+
LOGIN_REQUIRED: "true",
|
|
45
|
+
AUTH_CHECKED: "1"
|
|
46
|
+
};
|
|
47
|
+
var middlewareMatcher = [
|
|
48
|
+
"/((?!api|_next/static|_next/image|_next/webpack-hmr|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js)$).*)",
|
|
49
|
+
"/login"
|
|
50
|
+
];
|
|
51
|
+
var config = {
|
|
52
|
+
matcher: middlewareMatcher
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/services/utils/cookie-utils.ts
|
|
56
|
+
import Cookies from "js-cookie";
|
|
57
|
+
var _CookieUtils = class _CookieUtils {
|
|
58
|
+
/**
|
|
59
|
+
* Get the access token cookie key from environment variables
|
|
60
|
+
*/
|
|
61
|
+
static getAccessTokenKey() {
|
|
62
|
+
return process.env.AUTH_ACCESS_TOKEN_KEY || "auth_access_token";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the refresh token cookie key from environment variables
|
|
66
|
+
*/
|
|
67
|
+
static getRefreshTokenKey() {
|
|
68
|
+
return process.env.AUTH_REFRESH_TOKEN_KEY || "auth_refresh_token";
|
|
69
|
+
}
|
|
70
|
+
// Use current domain in development
|
|
71
|
+
/**
|
|
72
|
+
* Get root domain for subdomain support
|
|
73
|
+
*/
|
|
74
|
+
static getRootDomain() {
|
|
75
|
+
if (typeof window === "undefined") return void 0;
|
|
76
|
+
const hostname = window.location.hostname;
|
|
77
|
+
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
const parts = hostname.split(".");
|
|
84
|
+
if (parts.length >= 2) {
|
|
85
|
+
return `.${parts.slice(-2).join(".")}`;
|
|
86
|
+
}
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get common cookie options
|
|
91
|
+
*/
|
|
92
|
+
static getCookieOptions() {
|
|
93
|
+
return {
|
|
94
|
+
path: "/",
|
|
95
|
+
sameSite: "strict",
|
|
96
|
+
secure: process.env.NODE_ENV === "production",
|
|
97
|
+
// Only secure in production
|
|
98
|
+
domain: this.COOKIE_DOMAIN
|
|
99
|
+
// Support subdomains
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if running on client side
|
|
104
|
+
*/
|
|
105
|
+
static isClientSide() {
|
|
106
|
+
return typeof window !== "undefined";
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if running on server side
|
|
110
|
+
*/
|
|
111
|
+
static isServerSide() {
|
|
112
|
+
return typeof window === "undefined";
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Dynamically import server action for server-side operations
|
|
116
|
+
*/
|
|
117
|
+
static async getServerTokens() {
|
|
118
|
+
try {
|
|
119
|
+
const { getAuthTokens } = await import("mailsentry-auth/server");
|
|
120
|
+
return await getAuthTokens();
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error("Failed to get server tokens:", error);
|
|
123
|
+
return { accessToken: null, refreshToken: null };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Set access token in cookie (client-side only)
|
|
128
|
+
*/
|
|
129
|
+
static setAccessToken(token) {
|
|
130
|
+
if (this.isServerSide()) {
|
|
131
|
+
console.warn("setAccessToken called on server side - use server actions instead");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const options = this.getCookieOptions();
|
|
135
|
+
Cookies.set(this.getAccessTokenKey(), token, options);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Set refresh token in cookie (client-side only)
|
|
139
|
+
*/
|
|
140
|
+
static setRefreshToken(token) {
|
|
141
|
+
if (this.isServerSide()) {
|
|
142
|
+
console.warn("setRefreshToken called on server side - use server actions instead");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const options = this.getCookieOptions();
|
|
146
|
+
Cookies.set(this.getRefreshTokenKey(), token, options);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get access token - automatically handles client/server side
|
|
150
|
+
*/
|
|
151
|
+
static async getAccessToken() {
|
|
152
|
+
if (this.isClientSide()) {
|
|
153
|
+
return Cookies.get(this.getAccessTokenKey()) || null;
|
|
154
|
+
} else {
|
|
155
|
+
const { accessToken } = await this.getServerTokens();
|
|
156
|
+
return accessToken;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get refresh token - automatically handles client/server side
|
|
161
|
+
*/
|
|
162
|
+
static async getRefreshToken() {
|
|
163
|
+
if (this.isClientSide()) {
|
|
164
|
+
return Cookies.get(this.getRefreshTokenKey()) || null;
|
|
165
|
+
} else {
|
|
166
|
+
const { refreshToken } = await this.getServerTokens();
|
|
167
|
+
return refreshToken;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get both tokens - automatically handles client/server side
|
|
172
|
+
*/
|
|
173
|
+
static async getTokens() {
|
|
174
|
+
if (this.isClientSide()) {
|
|
175
|
+
return {
|
|
176
|
+
accessToken: Cookies.get(this.getAccessTokenKey()) || null,
|
|
177
|
+
refreshToken: Cookies.get(this.getRefreshTokenKey()) || null
|
|
178
|
+
};
|
|
179
|
+
} else {
|
|
180
|
+
return await this.getServerTokens();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clear all authentication cookies (client-side only)
|
|
185
|
+
*/
|
|
186
|
+
static clearAuthCookies() {
|
|
187
|
+
if (this.isServerSide()) {
|
|
188
|
+
console.warn("clearAuthCookies called on server side - use server actions instead");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const removeOptions = {
|
|
192
|
+
path: "/",
|
|
193
|
+
domain: this.COOKIE_DOMAIN
|
|
194
|
+
};
|
|
195
|
+
Cookies.remove(this.getAccessTokenKey(), removeOptions);
|
|
196
|
+
Cookies.remove(this.getRefreshTokenKey(), removeOptions);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if cookies are supported/enabled (client-side only)
|
|
200
|
+
*/
|
|
201
|
+
static areCookiesEnabled() {
|
|
202
|
+
if (this.isServerSide()) {
|
|
203
|
+
console.warn("areCookiesEnabled called on server side - not applicable");
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const testKey = "test_cookie_support";
|
|
208
|
+
const testOptions = this.getCookieOptions();
|
|
209
|
+
Cookies.set(testKey, "test", testOptions);
|
|
210
|
+
const value = Cookies.get(testKey);
|
|
211
|
+
Cookies.remove(testKey, {
|
|
212
|
+
path: "/",
|
|
213
|
+
domain: this.COOKIE_DOMAIN
|
|
214
|
+
});
|
|
215
|
+
return value === "test";
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get all authentication cookies - automatically handles client/server side
|
|
222
|
+
*/
|
|
223
|
+
static async getAllAuthCookies() {
|
|
224
|
+
if (this.isClientSide()) {
|
|
225
|
+
return {
|
|
226
|
+
accessToken: Cookies.get(this.getAccessTokenKey()) || "",
|
|
227
|
+
refreshToken: Cookies.get(this.getRefreshTokenKey()) || ""
|
|
228
|
+
};
|
|
229
|
+
} else {
|
|
230
|
+
const { accessToken, refreshToken } = await this.getServerTokens();
|
|
231
|
+
return {
|
|
232
|
+
accessToken: accessToken || "",
|
|
233
|
+
refreshToken: refreshToken || ""
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Set multiple tokens at once (client-side only)
|
|
239
|
+
*/
|
|
240
|
+
static setTokens(accessToken, refreshToken) {
|
|
241
|
+
if (this.isServerSide()) {
|
|
242
|
+
console.warn("setTokens called on server side - use server actions instead");
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.setAccessToken(accessToken);
|
|
246
|
+
this.setRefreshToken(refreshToken);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Check if user has valid tokens - automatically handles client/server side
|
|
250
|
+
*/
|
|
251
|
+
static async hasValidTokens() {
|
|
252
|
+
const tokens = await this.getTokens();
|
|
253
|
+
return !!(tokens.accessToken && tokens.refreshToken);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get current domain information for debugging
|
|
257
|
+
*/
|
|
258
|
+
static getDomainInfo() {
|
|
259
|
+
if (this.isServerSide()) {
|
|
260
|
+
return {
|
|
261
|
+
error: "Server-side rendering - no domain info available",
|
|
262
|
+
environment: process.env.NODE_ENV || "unknown",
|
|
263
|
+
recommendation: "Use server actions for server-side cookie access"
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
hostname: window.location.hostname,
|
|
268
|
+
domain: this.COOKIE_DOMAIN || "current domain",
|
|
269
|
+
environment: process.env.NODE_ENV || "unknown",
|
|
270
|
+
protocol: window.location.protocol
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
// Domain configuration
|
|
275
|
+
_CookieUtils.COOKIE_DOMAIN = process.env.NODE_ENV === "production" ? _CookieUtils.getRootDomain() : void 0;
|
|
276
|
+
var CookieUtils = _CookieUtils;
|
|
277
|
+
|
|
278
|
+
// src/services/utils/localstorage-utils.ts
|
|
279
|
+
var LocalStorageUtils = class {
|
|
280
|
+
// 5 minutes
|
|
281
|
+
/**
|
|
282
|
+
* Check if localStorage is available
|
|
283
|
+
*/
|
|
284
|
+
static isAvailable() {
|
|
285
|
+
try {
|
|
286
|
+
if (typeof window === "undefined") return false;
|
|
287
|
+
localStorage.setItem("test", "test");
|
|
288
|
+
localStorage.removeItem("test");
|
|
289
|
+
return true;
|
|
290
|
+
} catch (e) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Save user profile to localStorage with timestamp
|
|
296
|
+
*/
|
|
297
|
+
static saveUserProfile(userProfile) {
|
|
298
|
+
if (!this.isAvailable() || !userProfile) return false;
|
|
299
|
+
try {
|
|
300
|
+
localStorage.setItem(this.USER_PROFILE_STORAGE_KEY, JSON.stringify(userProfile));
|
|
301
|
+
localStorage.setItem(this.USER_PROFILE_TIMESTAMP_KEY, Date.now().toString());
|
|
302
|
+
return true;
|
|
303
|
+
} catch (e) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Get user profile from localStorage with cache validation
|
|
309
|
+
*/
|
|
310
|
+
static getUserProfile(cacheDuration = this.DEFAULT_CACHE_DURATION) {
|
|
311
|
+
if (!this.isAvailable()) return null;
|
|
312
|
+
try {
|
|
313
|
+
const userProfileData = localStorage.getItem(this.USER_PROFILE_STORAGE_KEY);
|
|
314
|
+
const timestamp = localStorage.getItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
315
|
+
if (!userProfileData || !timestamp) return null;
|
|
316
|
+
const cacheAge = Date.now() - parseInt(timestamp);
|
|
317
|
+
if (cacheAge >= cacheDuration) {
|
|
318
|
+
this.clearUserProfile();
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
return JSON.parse(userProfileData);
|
|
322
|
+
} catch (e) {
|
|
323
|
+
this.clearUserProfile();
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Clear user profile data from localStorage
|
|
329
|
+
*/
|
|
330
|
+
static clearUserProfile() {
|
|
331
|
+
if (!this.isAvailable()) return false;
|
|
332
|
+
try {
|
|
333
|
+
localStorage.removeItem(this.USER_PROFILE_STORAGE_KEY);
|
|
334
|
+
localStorage.removeItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
335
|
+
return true;
|
|
336
|
+
} catch (e) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Check if cached user profile is still valid
|
|
342
|
+
*/
|
|
343
|
+
static isCacheValid(cacheDuration = this.DEFAULT_CACHE_DURATION) {
|
|
344
|
+
if (!this.isAvailable()) return false;
|
|
345
|
+
try {
|
|
346
|
+
const timestamp = localStorage.getItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
347
|
+
if (!timestamp) return false;
|
|
348
|
+
const cacheAge = Date.now() - parseInt(timestamp);
|
|
349
|
+
return cacheAge < cacheDuration;
|
|
350
|
+
} catch (e) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
LocalStorageUtils.USER_PROFILE_STORAGE_KEY = "user_profile_data";
|
|
356
|
+
LocalStorageUtils.USER_PROFILE_TIMESTAMP_KEY = "user_profile_timestamp";
|
|
357
|
+
LocalStorageUtils.DEFAULT_CACHE_DURATION = 5 * 60 * 1e3;
|
|
358
|
+
|
|
359
|
+
// src/services/api/endpoints.ts
|
|
360
|
+
var AUTH_ENDPOINTS = {
|
|
361
|
+
// Email existence check
|
|
362
|
+
CHECK_EMAIL_EXISTS: (email) => `/auth/user/exist-email/${encodeURIComponent(email)}`,
|
|
363
|
+
// User authentication
|
|
364
|
+
LOGIN: "/auth/user/login",
|
|
365
|
+
VERIFY_EMAIL: "/auth/user/verify",
|
|
366
|
+
// User profile
|
|
367
|
+
GET_USER_PROFILE: "/auth/user/profile",
|
|
368
|
+
// User logout
|
|
369
|
+
LOGOUT: "/auth/logout"
|
|
370
|
+
};
|
|
371
|
+
var EndpointBuilder = class {
|
|
372
|
+
};
|
|
373
|
+
EndpointBuilder.auth = AUTH_ENDPOINTS;
|
|
374
|
+
|
|
375
|
+
// src/services/auth/patterns/command/auth-result-factory.ts
|
|
376
|
+
var AuthResultFactory = class {
|
|
377
|
+
/**
|
|
378
|
+
* Creates a successful authentication result
|
|
379
|
+
*/
|
|
380
|
+
static createSuccess(message, data) {
|
|
381
|
+
return {
|
|
382
|
+
success: true,
|
|
383
|
+
data,
|
|
384
|
+
message
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Creates a failed authentication result
|
|
389
|
+
*/
|
|
390
|
+
static createFailure(message, error) {
|
|
391
|
+
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : error ? String(error) : "Authentication failed";
|
|
392
|
+
return {
|
|
393
|
+
success: false,
|
|
394
|
+
error: errorMessage,
|
|
395
|
+
message
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
// src/services/auth/patterns/logger/development-logger.ts
|
|
401
|
+
var DevelopmentLogger = class {
|
|
402
|
+
log(message, data) {
|
|
403
|
+
console.log(message, data);
|
|
404
|
+
}
|
|
405
|
+
warn(message, data) {
|
|
406
|
+
console.warn(message, data);
|
|
407
|
+
}
|
|
408
|
+
error(message, data) {
|
|
409
|
+
console.error(message, data);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// src/services/auth/patterns/logger/production-logger.ts
|
|
414
|
+
var ProductionLogger = class {
|
|
415
|
+
log(_message, _data) {
|
|
416
|
+
}
|
|
417
|
+
warn(_message, _data) {
|
|
418
|
+
}
|
|
419
|
+
error(_message, _data) {
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// src/services/auth/patterns/logger/logger-factory.ts
|
|
424
|
+
var LoggerFactory = class {
|
|
425
|
+
static create(environment) {
|
|
426
|
+
const env = environment || process.env.NODE_ENV || "development";
|
|
427
|
+
const loggerFactory = this.loggers.get(env) || this.loggers.get("development");
|
|
428
|
+
return loggerFactory();
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
LoggerFactory.loggers = /* @__PURE__ */ new Map([
|
|
432
|
+
["development", () => new DevelopmentLogger()],
|
|
433
|
+
["production", () => new ProductionLogger()],
|
|
434
|
+
["test", () => new DevelopmentLogger()]
|
|
435
|
+
]);
|
|
436
|
+
|
|
437
|
+
// src/services/auth/patterns/strategy/signup-flow-strategy.ts
|
|
438
|
+
var SignupFlowStrategy = class {
|
|
439
|
+
constructor(authService, tokenManager) {
|
|
440
|
+
this.authService = authService;
|
|
441
|
+
this.tokenManager = tokenManager;
|
|
442
|
+
}
|
|
443
|
+
async execute(credentials) {
|
|
444
|
+
const loginResult = await this.authService.login(credentials);
|
|
445
|
+
if (!loginResult.data.isVerifiedEmail) {
|
|
446
|
+
return AuthResultFactory.createFailure("Email verification required");
|
|
447
|
+
}
|
|
448
|
+
return this.handleSuccessfulAuthentication(loginResult);
|
|
449
|
+
}
|
|
450
|
+
async handleSuccessfulAuthentication(loginResult) {
|
|
451
|
+
if (loginResult.data.accessToken) {
|
|
452
|
+
this.tokenManager.saveTokens(
|
|
453
|
+
loginResult.data.accessToken,
|
|
454
|
+
loginResult.data.refreshToken
|
|
455
|
+
);
|
|
456
|
+
LoggerFactory.create("development").log("Tokens saved successfully:", {
|
|
457
|
+
hasAccessToken: !!loginResult.data.accessToken,
|
|
458
|
+
hasRefreshToken: !!loginResult.data.refreshToken,
|
|
459
|
+
domainInfo: this.tokenManager.getDomainInfo()
|
|
460
|
+
});
|
|
461
|
+
const profile = await this.authService.getUserProfile();
|
|
462
|
+
return AuthResultFactory.createSuccess("Login successful", __spreadProps(__spreadValues({}, loginResult.data), {
|
|
463
|
+
profile: profile.data,
|
|
464
|
+
tokenInfo: {
|
|
465
|
+
domain: this.tokenManager.getDomainInfo().domain
|
|
466
|
+
}
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
return AuthResultFactory.createFailure("Authentication failed - no access token received");
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/services/auth/patterns/strategy/existing-user-login-strategy.ts
|
|
474
|
+
var ExistingUserLoginStrategy = class {
|
|
475
|
+
constructor(authService, tokenManager) {
|
|
476
|
+
this.authService = authService;
|
|
477
|
+
this.tokenManager = tokenManager;
|
|
478
|
+
}
|
|
479
|
+
async execute(credentials) {
|
|
480
|
+
const loginResult = await this.authService.login(credentials);
|
|
481
|
+
return this.handleSuccessfulAuthentication(loginResult);
|
|
482
|
+
}
|
|
483
|
+
async handleSuccessfulAuthentication(loginResult) {
|
|
484
|
+
if (loginResult.data.accessToken) {
|
|
485
|
+
this.tokenManager.saveTokens(
|
|
486
|
+
loginResult.data.accessToken,
|
|
487
|
+
loginResult.data.refreshToken
|
|
488
|
+
);
|
|
489
|
+
LoggerFactory.create("development").log("Tokens saved successfully:", {
|
|
490
|
+
hasAccessToken: !!loginResult.data.accessToken,
|
|
491
|
+
hasRefreshToken: !!loginResult.data.refreshToken,
|
|
492
|
+
domainInfo: this.tokenManager.getDomainInfo()
|
|
493
|
+
});
|
|
494
|
+
const profile = await this.authService.getUserProfile();
|
|
495
|
+
return AuthResultFactory.createSuccess("Login successful", __spreadProps(__spreadValues({}, loginResult.data), {
|
|
496
|
+
profile: profile.data,
|
|
497
|
+
tokenInfo: {
|
|
498
|
+
domain: this.tokenManager.getDomainInfo().domain
|
|
499
|
+
}
|
|
500
|
+
}));
|
|
501
|
+
}
|
|
502
|
+
return AuthResultFactory.createFailure("Authentication failed - no access token received");
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// src/services/auth/patterns/strategy/login-flow-strategy-factory.ts
|
|
507
|
+
var LoginFlowStrategyFactory = class {
|
|
508
|
+
static createStrategy(action, authService, tokenManager) {
|
|
509
|
+
const strategyFactory = this.strategies.get(action);
|
|
510
|
+
if (!strategyFactory) {
|
|
511
|
+
throw new Error(`No strategy found for action: ${action}`);
|
|
512
|
+
}
|
|
513
|
+
return strategyFactory(authService, tokenManager);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
LoginFlowStrategyFactory.strategies = /* @__PURE__ */ new Map([
|
|
517
|
+
["signup" /* SIGNUP */, (authService, tokenManager) => new SignupFlowStrategy(authService, tokenManager)],
|
|
518
|
+
["login" /* LOGIN */, (authService, tokenManager) => new ExistingUserLoginStrategy(authService, tokenManager)]
|
|
519
|
+
]);
|
|
520
|
+
|
|
521
|
+
// src/services/auth/patterns/state/authenticated-state.ts
|
|
522
|
+
var AuthenticatedState = class {
|
|
523
|
+
async getStatus(tokenManager) {
|
|
524
|
+
const authStatus = await this.buildAuthStatus(tokenManager, true);
|
|
525
|
+
return AuthResultFactory.createSuccess("User is authenticated", authStatus);
|
|
526
|
+
}
|
|
527
|
+
async buildAuthStatus(tokenManager, isAuthenticated) {
|
|
528
|
+
return {
|
|
529
|
+
isAuthenticated,
|
|
530
|
+
hasAccessToken: !!await tokenManager.getAccessToken(),
|
|
531
|
+
hasRefreshToken: !!await tokenManager.getRefreshToken(),
|
|
532
|
+
cookiesSupported: tokenManager.areCookiesSupported(),
|
|
533
|
+
domainInfo: tokenManager.getDomainInfo()
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// src/services/auth/patterns/state/unauthenticated-state.ts
|
|
539
|
+
var UnauthenticatedState = class {
|
|
540
|
+
async getStatus(tokenManager) {
|
|
541
|
+
const authStatus = await this.buildAuthStatus(tokenManager, false);
|
|
542
|
+
return AuthResultFactory.createFailure("User is not authenticated", authStatus);
|
|
543
|
+
}
|
|
544
|
+
async buildAuthStatus(tokenManager, isAuthenticated) {
|
|
545
|
+
return {
|
|
546
|
+
isAuthenticated,
|
|
547
|
+
hasAccessToken: !!await tokenManager.getAccessToken(),
|
|
548
|
+
hasRefreshToken: !!await tokenManager.getRefreshToken(),
|
|
549
|
+
cookiesSupported: tokenManager.areCookiesSupported(),
|
|
550
|
+
domainInfo: tokenManager.getDomainInfo()
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
// src/services/auth/patterns/state/authentication-status-context.ts
|
|
556
|
+
var AuthenticationStatusContext = class {
|
|
557
|
+
static async getStatus(tokenManager) {
|
|
558
|
+
const isAuthenticated = !!await tokenManager.getAccessToken();
|
|
559
|
+
const state = this.states.get(isAuthenticated) || new UnauthenticatedState();
|
|
560
|
+
return await state.getStatus(tokenManager);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
AuthenticationStatusContext.states = /* @__PURE__ */ new Map([
|
|
564
|
+
[true, new AuthenticatedState()],
|
|
565
|
+
[false, new UnauthenticatedState()]
|
|
566
|
+
]);
|
|
567
|
+
|
|
568
|
+
// src/middlewares/handlers/base-middleware-handler.ts
|
|
569
|
+
var BaseMiddlewareHandler = class {
|
|
570
|
+
/**
|
|
571
|
+
* Check if user has both access and refresh tokens
|
|
572
|
+
*/
|
|
573
|
+
hasAuthenticationCookies(cookies) {
|
|
574
|
+
const { accessTokenKey, refreshTokenKey } = this.getAuthCookieKeys();
|
|
575
|
+
return cookies.has(accessTokenKey) && cookies.has(refreshTokenKey);
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get authentication cookie keys from environment variables with fallbacks
|
|
579
|
+
*/
|
|
580
|
+
getAuthCookieKeys() {
|
|
581
|
+
return {
|
|
582
|
+
accessTokenKey: process.env.AUTH_ACCESS_TOKEN_KEY || "",
|
|
583
|
+
refreshTokenKey: process.env.AUTH_REFRESH_TOKEN_KEY || ""
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Create a redirect response to the dashboard
|
|
588
|
+
*/
|
|
589
|
+
redirectToDashboard(context) {
|
|
590
|
+
const dashboardUrl = new URL(MiddlewareConfig.PROTECTED_ROUTES.DASHBOARD, context.request.url);
|
|
591
|
+
return NextResponse.redirect(dashboardUrl);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Create a redirect response to login with authentication parameters
|
|
595
|
+
* Includes loop prevention - won't redirect if already has auth_checked parameter
|
|
596
|
+
*/
|
|
597
|
+
redirectToLoginWithParams(context) {
|
|
598
|
+
const loginUrl = new URL(MiddlewareConfig.AUTH_ROUTES.LOGIN, context.request.url);
|
|
599
|
+
if (context.searchParams.get(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED) === MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED) {
|
|
600
|
+
return NextResponse.next();
|
|
601
|
+
}
|
|
602
|
+
loginUrl.searchParams.set(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED, MiddlewareConfig.QUERY_VALUES.LOGIN_REQUIRED);
|
|
603
|
+
loginUrl.searchParams.set(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED, MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED);
|
|
604
|
+
return NextResponse.redirect(loginUrl);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Create a redirect response with custom URL and auth parameters
|
|
608
|
+
* Includes loop prevention - won't redirect if target already has auth_checked parameter
|
|
609
|
+
*/
|
|
610
|
+
redirectWithAuthParams(targetUrl) {
|
|
611
|
+
const url = new URL(targetUrl.toString());
|
|
612
|
+
if (url.searchParams.get(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED) === MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED) {
|
|
613
|
+
return NextResponse.next();
|
|
614
|
+
}
|
|
615
|
+
url.searchParams.set(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED, MiddlewareConfig.QUERY_VALUES.LOGIN_REQUIRED);
|
|
616
|
+
url.searchParams.set(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED, MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED);
|
|
617
|
+
return NextResponse.redirect(url);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Check if the current path is a dashboard route
|
|
621
|
+
*/
|
|
622
|
+
isDashboardRoute(pathname) {
|
|
623
|
+
return pathname.startsWith(MiddlewareConfig.PROTECTED_ROUTES.DASHBOARD);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Check if the current path is the login page
|
|
627
|
+
*/
|
|
628
|
+
isLoginRoute(pathname) {
|
|
629
|
+
return pathname === MiddlewareConfig.AUTH_ROUTES.LOGIN;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Check if request method is allowed
|
|
633
|
+
*/
|
|
634
|
+
isAllowedMethod(method) {
|
|
635
|
+
return MiddlewareConfig.ALLOWED_METHODS.includes(method);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Check if the request has auth checked parameter (loop prevention)
|
|
639
|
+
*/
|
|
640
|
+
hasAuthCheckedParam(searchParams) {
|
|
641
|
+
return searchParams.get(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED) === MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Continue to next handler
|
|
645
|
+
*/
|
|
646
|
+
continue() {
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Allow request to proceed
|
|
651
|
+
*/
|
|
652
|
+
allow() {
|
|
653
|
+
return NextResponse.next();
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Create a redirect response with cleaned URL (removes auth-related query parameters)
|
|
657
|
+
*/
|
|
658
|
+
redirectWithCleanUrl(context) {
|
|
659
|
+
const cleanUrl = new URL(context.nextUrl.toString());
|
|
660
|
+
cleanUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED);
|
|
661
|
+
cleanUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
662
|
+
return NextResponse.redirect(cleanUrl);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Check if URL has auth-related query parameters that need cleanup
|
|
666
|
+
*/
|
|
667
|
+
hasAuthQueryParams(searchParams) {
|
|
668
|
+
return searchParams.has(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED) || searchParams.has(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Make an authenticated GET API call to getUserProfile endpoint
|
|
672
|
+
*/
|
|
673
|
+
async makeAuthenticatedApiCall(cookies) {
|
|
674
|
+
var _a;
|
|
675
|
+
const { accessTokenKey } = this.getAuthCookieKeys();
|
|
676
|
+
const accessToken = (_a = cookies.get(accessTokenKey)) == null ? void 0 : _a.value;
|
|
677
|
+
if (!accessToken) {
|
|
678
|
+
throw new Error("Access token not found in cookies");
|
|
679
|
+
}
|
|
680
|
+
const baseURL = process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
681
|
+
const apiKey = process.env.NEXT_PUBLIC_API_KEY || "";
|
|
682
|
+
const origin = process.env.NEXT_PUBLIC_API_ORIGIN || "";
|
|
683
|
+
const url = `${baseURL}${AUTH_ENDPOINTS.GET_USER_PROFILE}`;
|
|
684
|
+
const response = await fetch(url, {
|
|
685
|
+
method: "GET" /* GET */,
|
|
686
|
+
headers: {
|
|
687
|
+
"Content-Type": "application/json",
|
|
688
|
+
"Origin": origin,
|
|
689
|
+
"x-api-key": apiKey,
|
|
690
|
+
"authorization": `Bearer ${accessToken}`
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
return response;
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// src/middlewares/handlers/method-filter-handler.ts
|
|
698
|
+
var MethodFilterHandler = class extends BaseMiddlewareHandler {
|
|
699
|
+
handle(context) {
|
|
700
|
+
const { method } = context;
|
|
701
|
+
if (!this.isAllowedMethod(method)) {
|
|
702
|
+
return this.allow();
|
|
703
|
+
}
|
|
704
|
+
return this.continue();
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
// src/middlewares/handlers/route-protection-handler.ts
|
|
709
|
+
var RouteProtectionHandler = class extends BaseMiddlewareHandler {
|
|
710
|
+
handle(context) {
|
|
711
|
+
const { pathname } = context;
|
|
712
|
+
if (!this.isDashboardRoute(pathname)) {
|
|
713
|
+
return this.continue();
|
|
714
|
+
}
|
|
715
|
+
return this.continue();
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// src/middlewares/handlers/authentication-handler.ts
|
|
720
|
+
var AuthenticationHandler = class extends BaseMiddlewareHandler {
|
|
721
|
+
async handle(context) {
|
|
722
|
+
const { cookies, nextUrl, pathname } = context;
|
|
723
|
+
const hasAuthCookies = this.hasAuthenticationCookies(cookies);
|
|
724
|
+
if (!hasAuthCookies) {
|
|
725
|
+
if (this.isDashboardRoute(pathname)) {
|
|
726
|
+
return this.redirectWithAuthParams(nextUrl);
|
|
727
|
+
}
|
|
728
|
+
return this.continue();
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
const response = await this.makeAuthenticatedApiCall(cookies);
|
|
732
|
+
if (!response.ok) {
|
|
733
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
734
|
+
}
|
|
735
|
+
await response.json();
|
|
736
|
+
if (this.hasAuthQueryParams(context.searchParams)) {
|
|
737
|
+
return this.redirectWithCleanUrl(context);
|
|
738
|
+
}
|
|
739
|
+
return this.allow();
|
|
740
|
+
} catch (error) {
|
|
741
|
+
console.log("JWT validation failed:", error instanceof Error ? error.message : "Unknown error");
|
|
742
|
+
if (this.isDashboardRoute(pathname)) {
|
|
743
|
+
return this.redirectWithAuthParams(nextUrl);
|
|
744
|
+
}
|
|
745
|
+
return this.continue();
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
// src/middlewares/handlers/login-redirect-handler.ts
|
|
751
|
+
var LoginRedirectHandler = class extends BaseMiddlewareHandler {
|
|
752
|
+
handle(context) {
|
|
753
|
+
const { pathname, cookies } = context;
|
|
754
|
+
if (!this.isLoginRoute(pathname)) {
|
|
755
|
+
return this.continue();
|
|
756
|
+
}
|
|
757
|
+
const isAuthenticated = this.hasAuthenticationCookies(cookies);
|
|
758
|
+
if (isAuthenticated) {
|
|
759
|
+
return this.redirectToDashboard(context);
|
|
760
|
+
}
|
|
761
|
+
return this.continue();
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// src/middlewares/handlers/middleware-chain.ts
|
|
766
|
+
import { NextResponse as NextResponse2 } from "next/server";
|
|
767
|
+
var MiddlewareChain = class {
|
|
768
|
+
constructor() {
|
|
769
|
+
this.handlers = [];
|
|
770
|
+
}
|
|
771
|
+
addHandler(handler) {
|
|
772
|
+
this.handlers.push(handler);
|
|
773
|
+
return this;
|
|
774
|
+
}
|
|
775
|
+
async process(context) {
|
|
776
|
+
for (const handler of this.handlers) {
|
|
777
|
+
const result = await handler.handle(context);
|
|
778
|
+
if (result !== null) {
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return NextResponse2.next();
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/middleware.ts
|
|
787
|
+
async function middleware(req) {
|
|
788
|
+
const context = {
|
|
789
|
+
request: req,
|
|
790
|
+
method: req.method,
|
|
791
|
+
pathname: req.nextUrl.pathname,
|
|
792
|
+
searchParams: req.nextUrl.searchParams,
|
|
793
|
+
cookies: req.cookies,
|
|
794
|
+
nextUrl: req.nextUrl
|
|
795
|
+
};
|
|
796
|
+
const chain = new MiddlewareChain().addHandler(new MethodFilterHandler()).addHandler(new RouteProtectionHandler()).addHandler(new LoginRedirectHandler()).addHandler(new AuthenticationHandler());
|
|
797
|
+
return await chain.process(context);
|
|
798
|
+
}
|
|
799
|
+
export {
|
|
800
|
+
config,
|
|
801
|
+
middleware,
|
|
802
|
+
middlewareMatcher
|
|
803
|
+
};
|