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.
@@ -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
+ };