mailsentry-auth 0.1.5 → 0.1.7
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/dist/index.d.mts +36 -40
- package/dist/index.d.ts +36 -40
- package/dist/index.js +86 -108
- package/dist/index.mjs +86 -108
- package/dist/middleware.mjs +94 -394
- package/dist/utils/cookie-utils.d.mts +86 -0
- package/dist/utils/cookie-utils.d.ts +86 -0
- package/dist/utils/cookie-utils.js +97 -22
- package/dist/utils/cookie-utils.mjs +97 -22
- package/package.json +3 -3
package/dist/middleware.mjs
CHANGED
|
@@ -1,33 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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";
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import Cookies from 'js-cookie';
|
|
23
3
|
|
|
24
4
|
// src/config/middleware.ts
|
|
25
|
-
var
|
|
5
|
+
var _MiddlewareConfig = class _MiddlewareConfig {
|
|
26
6
|
/**
|
|
27
7
|
* Get the base domain from environment or use default
|
|
28
8
|
*/
|
|
29
9
|
static getBaseDomain() {
|
|
30
|
-
return process.env.NEXT_PUBLIC_BASE_DOMAIN
|
|
10
|
+
return process.env.NEXT_PUBLIC_BASE_DOMAIN;
|
|
31
11
|
}
|
|
32
12
|
/**
|
|
33
13
|
* Get the protocol based on environment
|
|
@@ -35,35 +15,44 @@ var MiddlewareConfig = class {
|
|
|
35
15
|
static getProtocol() {
|
|
36
16
|
return process.env.NODE_ENV === "production" ? "https" : "http";
|
|
37
17
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Get the dashboard subdomain URL
|
|
40
|
-
*/
|
|
41
|
-
static getDashboardUrl(path = "") {
|
|
42
|
-
return `${this.getProtocol()}://${this.SUBDOMAINS.DASHBOARD}.${this.getBaseDomain()}${path}`;
|
|
43
|
-
}
|
|
44
18
|
};
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
19
|
+
// Common constants
|
|
20
|
+
_MiddlewareConfig.CONSTANTS = {
|
|
21
|
+
LOGIN_PATH: "/login",
|
|
22
|
+
DASHBOARD_SUBDOMAIN: "dashboard",
|
|
23
|
+
PUBLIC_PATH: "/public",
|
|
24
|
+
PUBLIC_API_PATH: "/api/public"
|
|
48
25
|
};
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
26
|
+
// Route Protection Configuration
|
|
27
|
+
// PATTERNS_TO_PROTECT: Routes that require authentication (whitelist approach recommended for security)
|
|
28
|
+
// PATTERNS_TO_EXCLUDE: Routes to explicitly exclude from protection (e.g., login page, public assets)
|
|
29
|
+
_MiddlewareConfig.PROTECTED_ROUTES = {
|
|
30
|
+
// Routes that MUST be protected
|
|
31
|
+
INCLUDE: [
|
|
32
|
+
"/"
|
|
33
|
+
// Protect everything by default (since dashboard.cutly.io/ is the root)
|
|
34
|
+
],
|
|
35
|
+
// Routes that should NEVER be protected (public)
|
|
36
|
+
EXCLUDE: [
|
|
37
|
+
_MiddlewareConfig.CONSTANTS.LOGIN_PATH,
|
|
38
|
+
_MiddlewareConfig.CONSTANTS.PUBLIC_PATH,
|
|
39
|
+
_MiddlewareConfig.CONSTANTS.PUBLIC_API_PATH
|
|
40
|
+
]
|
|
53
41
|
};
|
|
54
42
|
// HTTP methods to process
|
|
55
|
-
|
|
43
|
+
_MiddlewareConfig.ALLOWED_METHODS = ["GET", "HEAD"];
|
|
56
44
|
// Query parameters
|
|
57
|
-
|
|
45
|
+
_MiddlewareConfig.QUERY_PARAMS = {
|
|
58
46
|
LOGIN_REQUIRED: "sign_in_required",
|
|
59
47
|
AUTH_CHECKED: "auth_checked",
|
|
60
48
|
REDIRECT_URL: "redirect_url"
|
|
61
49
|
};
|
|
62
50
|
// Query parameter values
|
|
63
|
-
|
|
51
|
+
_MiddlewareConfig.QUERY_VALUES = {
|
|
64
52
|
LOGIN_REQUIRED: "true",
|
|
65
53
|
AUTH_CHECKED: "1"
|
|
66
54
|
};
|
|
55
|
+
var MiddlewareConfig = _MiddlewareConfig;
|
|
67
56
|
var middlewareMatcher = [
|
|
68
57
|
"/((?!api|_next/static|_next/image|_next/webpack-hmr|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js)$).*)"
|
|
69
58
|
];
|
|
@@ -71,20 +60,55 @@ var config = {
|
|
|
71
60
|
matcher: middlewareMatcher
|
|
72
61
|
};
|
|
73
62
|
|
|
63
|
+
// src/services/utils/url-utils.ts
|
|
64
|
+
var UrlUtils = class {
|
|
65
|
+
/**
|
|
66
|
+
* Extract subdomain from hostname
|
|
67
|
+
* Example: "dashboard.cutly.io" -> "dashboard"
|
|
68
|
+
*/
|
|
69
|
+
static getSubdomain(hostname) {
|
|
70
|
+
if (!hostname || /^\d{1,3}(\.\d{1,3}){3}/.test(hostname) || hostname.startsWith(MiddlewareConfig.getBaseDomain())) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const parts = hostname.split(".");
|
|
74
|
+
return parts.length >= 2 ? parts[0] : null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get root domain for cookie scope
|
|
78
|
+
* Example: "dashboard.cutly.io" -> ".cutly.io"
|
|
79
|
+
* Example: "cutly.io" -> ".cutly.io"
|
|
80
|
+
*/
|
|
81
|
+
static getRootDomain(hostname) {
|
|
82
|
+
if (!hostname || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const parts = hostname.split(".");
|
|
86
|
+
if (parts.length >= 2) {
|
|
87
|
+
return `.${parts.slice(-2).join(".")}`;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if URL has auth-related query parameters
|
|
93
|
+
*/
|
|
94
|
+
static hasAuthParams(url) {
|
|
95
|
+
return url.includes(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
74
99
|
// src/services/utils/cookie-utils.ts
|
|
75
|
-
import Cookies from "js-cookie";
|
|
76
100
|
var _CookieUtils = class _CookieUtils {
|
|
77
101
|
/**
|
|
78
|
-
* Get the access token cookie key
|
|
102
|
+
* Get the access token cookie key
|
|
79
103
|
*/
|
|
80
104
|
static getAccessTokenKey() {
|
|
81
|
-
return
|
|
105
|
+
return "auth_access_token";
|
|
82
106
|
}
|
|
83
107
|
/**
|
|
84
|
-
* Get the refresh token cookie key
|
|
108
|
+
* Get the refresh token cookie key
|
|
85
109
|
*/
|
|
86
110
|
static getRefreshTokenKey() {
|
|
87
|
-
return
|
|
111
|
+
return "auth_refresh_token";
|
|
88
112
|
}
|
|
89
113
|
/**
|
|
90
114
|
* Get root domain for subdomain support
|
|
@@ -92,26 +116,10 @@ var _CookieUtils = class _CookieUtils {
|
|
|
92
116
|
*/
|
|
93
117
|
static getRootDomain() {
|
|
94
118
|
if (typeof window === "undefined") {
|
|
95
|
-
const baseDomain = process.env.NEXT_PUBLIC_BASE_DOMAIN;
|
|
96
|
-
if (baseDomain && process.env.NODE_ENV === "production") {
|
|
97
|
-
return `.${baseDomain}`;
|
|
98
|
-
}
|
|
99
|
-
return void 0;
|
|
100
|
-
}
|
|
101
|
-
const hostname = window.location.hostname;
|
|
102
|
-
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
103
119
|
return void 0;
|
|
104
120
|
}
|
|
105
|
-
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
const parts = hostname.split(".");
|
|
109
|
-
if (parts.length >= 2) {
|
|
110
|
-
return `.${parts.slice(-2).join(".")}`;
|
|
111
|
-
}
|
|
112
|
-
return void 0;
|
|
121
|
+
return UrlUtils.getRootDomain(window.location.hostname) || void 0;
|
|
113
122
|
}
|
|
114
|
-
// Use current domain in development
|
|
115
123
|
/**
|
|
116
124
|
* Get common cookie options
|
|
117
125
|
*/
|
|
@@ -143,7 +151,7 @@ var _CookieUtils = class _CookieUtils {
|
|
|
143
151
|
*/
|
|
144
152
|
static async getServerTokens() {
|
|
145
153
|
try {
|
|
146
|
-
const { getAuthTokens } = await import(
|
|
154
|
+
const { getAuthTokens } = await import('mailsentry-auth/server');
|
|
147
155
|
return await getAuthTokens();
|
|
148
156
|
} catch (error) {
|
|
149
157
|
console.error("Failed to get server tokens:", error);
|
|
@@ -310,333 +318,30 @@ var _CookieUtils = class _CookieUtils {
|
|
|
310
318
|
}
|
|
311
319
|
};
|
|
312
320
|
// Domain configuration - computed once
|
|
313
|
-
_CookieUtils.COOKIE_DOMAIN =
|
|
321
|
+
_CookieUtils.COOKIE_DOMAIN = _CookieUtils.getRootDomain();
|
|
314
322
|
var CookieUtils = _CookieUtils;
|
|
315
323
|
|
|
316
|
-
// src/services/utils/localstorage-utils.ts
|
|
317
|
-
var LocalStorageUtils = class {
|
|
318
|
-
// 5 minutes
|
|
319
|
-
/**
|
|
320
|
-
* Check if localStorage is available
|
|
321
|
-
*/
|
|
322
|
-
static isAvailable() {
|
|
323
|
-
try {
|
|
324
|
-
if (typeof window === "undefined") return false;
|
|
325
|
-
localStorage.setItem("test", "test");
|
|
326
|
-
localStorage.removeItem("test");
|
|
327
|
-
return true;
|
|
328
|
-
} catch (e) {
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Save user profile to localStorage with timestamp
|
|
334
|
-
*/
|
|
335
|
-
static saveUserProfile(userProfile) {
|
|
336
|
-
if (!this.isAvailable() || !userProfile) return false;
|
|
337
|
-
try {
|
|
338
|
-
localStorage.setItem(this.USER_PROFILE_STORAGE_KEY, JSON.stringify(userProfile));
|
|
339
|
-
localStorage.setItem(this.USER_PROFILE_TIMESTAMP_KEY, Date.now().toString());
|
|
340
|
-
return true;
|
|
341
|
-
} catch (e) {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Get user profile from localStorage with cache validation
|
|
347
|
-
*/
|
|
348
|
-
static getUserProfile(cacheDuration = this.DEFAULT_CACHE_DURATION) {
|
|
349
|
-
if (!this.isAvailable()) return null;
|
|
350
|
-
try {
|
|
351
|
-
const userProfileData = localStorage.getItem(this.USER_PROFILE_STORAGE_KEY);
|
|
352
|
-
const timestamp = localStorage.getItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
353
|
-
if (!userProfileData || !timestamp) return null;
|
|
354
|
-
const cacheAge = Date.now() - parseInt(timestamp);
|
|
355
|
-
if (cacheAge >= cacheDuration) {
|
|
356
|
-
this.clearUserProfile();
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
return JSON.parse(userProfileData);
|
|
360
|
-
} catch (e) {
|
|
361
|
-
this.clearUserProfile();
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* Clear user profile data from localStorage
|
|
367
|
-
*/
|
|
368
|
-
static clearUserProfile() {
|
|
369
|
-
if (!this.isAvailable()) return false;
|
|
370
|
-
try {
|
|
371
|
-
localStorage.removeItem(this.USER_PROFILE_STORAGE_KEY);
|
|
372
|
-
localStorage.removeItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
373
|
-
return true;
|
|
374
|
-
} catch (e) {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Check if cached user profile is still valid
|
|
380
|
-
*/
|
|
381
|
-
static isCacheValid(cacheDuration = this.DEFAULT_CACHE_DURATION) {
|
|
382
|
-
if (!this.isAvailable()) return false;
|
|
383
|
-
try {
|
|
384
|
-
const timestamp = localStorage.getItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
385
|
-
if (!timestamp) return false;
|
|
386
|
-
const cacheAge = Date.now() - parseInt(timestamp);
|
|
387
|
-
return cacheAge < cacheDuration;
|
|
388
|
-
} catch (e) {
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
LocalStorageUtils.USER_PROFILE_STORAGE_KEY = "user_profile_data";
|
|
394
|
-
LocalStorageUtils.USER_PROFILE_TIMESTAMP_KEY = "user_profile_timestamp";
|
|
395
|
-
LocalStorageUtils.DEFAULT_CACHE_DURATION = 5 * 60 * 1e3;
|
|
396
|
-
|
|
397
|
-
// src/services/utils/url-utils.ts
|
|
398
|
-
var UrlUtils = class {
|
|
399
|
-
/**
|
|
400
|
-
* Extract subdomain from hostname or URL
|
|
401
|
-
* Example: "dashboard.cutly.io" -> "dashboard"
|
|
402
|
-
* Example: "dashboard.localhost" -> "dashboard"
|
|
403
|
-
*/
|
|
404
|
-
static getSubdomain(url) {
|
|
405
|
-
try {
|
|
406
|
-
const domain = new URL(`http://${url}`).hostname;
|
|
407
|
-
const parts = domain.split(".");
|
|
408
|
-
if (parts.length < 2 || domain === "localhost" || /^\d{1,3}(\.\d{1,3}){3}/.test(domain) || domain.startsWith(MiddlewareConfig.getBaseDomain())) {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
return parts[0] || null;
|
|
412
|
-
} catch (e) {
|
|
413
|
-
return null;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Check if URL has auth-related query parameters
|
|
418
|
-
*/
|
|
419
|
-
static hasAuthParams(url) {
|
|
420
|
-
return url.includes(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
|
|
424
324
|
// src/services/api/endpoints.ts
|
|
425
325
|
var AUTH_ENDPOINTS = {
|
|
426
|
-
// Email existence check
|
|
427
|
-
CHECK_EMAIL_EXISTS: (email) => `/auth/user/exist-email/${encodeURIComponent(email)}`,
|
|
428
|
-
// User authentication
|
|
429
|
-
LOGIN: "/auth/user/login",
|
|
430
|
-
VERIFY_EMAIL: "/auth/user/verify",
|
|
431
326
|
// User profile
|
|
432
|
-
GET_USER_PROFILE: "/auth/user/profile"
|
|
433
|
-
// User logout
|
|
434
|
-
LOGOUT: "/auth/logout"
|
|
435
|
-
};
|
|
436
|
-
var EndpointBuilder = class {
|
|
437
|
-
};
|
|
438
|
-
EndpointBuilder.auth = AUTH_ENDPOINTS;
|
|
439
|
-
|
|
440
|
-
// src/services/auth/patterns/command/auth-result-factory.ts
|
|
441
|
-
var AuthResultFactory = class {
|
|
442
|
-
/**
|
|
443
|
-
* Creates a successful authentication result
|
|
444
|
-
*/
|
|
445
|
-
static createSuccess(message, data) {
|
|
446
|
-
return {
|
|
447
|
-
success: true,
|
|
448
|
-
data,
|
|
449
|
-
message
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Creates a failed authentication result
|
|
454
|
-
*/
|
|
455
|
-
static createFailure(message, error) {
|
|
456
|
-
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : error ? String(error) : "Authentication failed";
|
|
457
|
-
return {
|
|
458
|
-
success: false,
|
|
459
|
-
error: errorMessage,
|
|
460
|
-
message
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
// src/services/auth/patterns/logger/development-logger.ts
|
|
466
|
-
var DevelopmentLogger = class {
|
|
467
|
-
log(message, data) {
|
|
468
|
-
console.log(message, data);
|
|
469
|
-
}
|
|
470
|
-
warn(message, data) {
|
|
471
|
-
console.warn(message, data);
|
|
472
|
-
}
|
|
473
|
-
error(message, data) {
|
|
474
|
-
console.error(message, data);
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
// src/services/auth/patterns/logger/production-logger.ts
|
|
479
|
-
var ProductionLogger = class {
|
|
480
|
-
log(_message, _data) {
|
|
481
|
-
}
|
|
482
|
-
warn(_message, _data) {
|
|
483
|
-
}
|
|
484
|
-
error(_message, _data) {
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
// src/services/auth/patterns/logger/logger-factory.ts
|
|
489
|
-
var LoggerFactory = class {
|
|
490
|
-
static create(environment) {
|
|
491
|
-
const env = environment || process.env.NODE_ENV || "development";
|
|
492
|
-
const loggerFactory = this.loggers.get(env) || this.loggers.get("development");
|
|
493
|
-
return loggerFactory();
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
LoggerFactory.loggers = /* @__PURE__ */ new Map([
|
|
497
|
-
["development", () => new DevelopmentLogger()],
|
|
498
|
-
["production", () => new ProductionLogger()],
|
|
499
|
-
["test", () => new DevelopmentLogger()]
|
|
500
|
-
]);
|
|
501
|
-
|
|
502
|
-
// src/services/auth/patterns/strategy/signup-flow-strategy.ts
|
|
503
|
-
var SignupFlowStrategy = class {
|
|
504
|
-
constructor(authService, tokenManager) {
|
|
505
|
-
this.authService = authService;
|
|
506
|
-
this.tokenManager = tokenManager;
|
|
507
|
-
}
|
|
508
|
-
async execute(credentials) {
|
|
509
|
-
const loginResult = await this.authService.login(credentials);
|
|
510
|
-
if (!loginResult.data.isVerifiedEmail) {
|
|
511
|
-
return AuthResultFactory.createFailure("Email verification required");
|
|
512
|
-
}
|
|
513
|
-
return this.handleSuccessfulAuthentication(loginResult);
|
|
514
|
-
}
|
|
515
|
-
async handleSuccessfulAuthentication(loginResult) {
|
|
516
|
-
if (loginResult.data.accessToken) {
|
|
517
|
-
this.tokenManager.saveTokens(
|
|
518
|
-
loginResult.data.accessToken,
|
|
519
|
-
loginResult.data.refreshToken
|
|
520
|
-
);
|
|
521
|
-
LoggerFactory.create("development").log("Tokens saved successfully:", {
|
|
522
|
-
hasAccessToken: !!loginResult.data.accessToken,
|
|
523
|
-
hasRefreshToken: !!loginResult.data.refreshToken,
|
|
524
|
-
domainInfo: this.tokenManager.getDomainInfo()
|
|
525
|
-
});
|
|
526
|
-
const profile = await this.authService.getUserProfile();
|
|
527
|
-
return AuthResultFactory.createSuccess("Login successful", __spreadProps(__spreadValues({}, loginResult.data), {
|
|
528
|
-
profile: profile.data,
|
|
529
|
-
tokenInfo: {
|
|
530
|
-
domain: this.tokenManager.getDomainInfo().domain
|
|
531
|
-
}
|
|
532
|
-
}));
|
|
533
|
-
}
|
|
534
|
-
return AuthResultFactory.createFailure("Authentication failed - no access token received");
|
|
535
|
-
}
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
// src/services/auth/patterns/strategy/existing-user-login-strategy.ts
|
|
539
|
-
var ExistingUserLoginStrategy = class {
|
|
540
|
-
constructor(authService, tokenManager) {
|
|
541
|
-
this.authService = authService;
|
|
542
|
-
this.tokenManager = tokenManager;
|
|
543
|
-
}
|
|
544
|
-
async execute(credentials) {
|
|
545
|
-
const loginResult = await this.authService.login(credentials);
|
|
546
|
-
return this.handleSuccessfulAuthentication(loginResult);
|
|
547
|
-
}
|
|
548
|
-
async handleSuccessfulAuthentication(loginResult) {
|
|
549
|
-
if (loginResult.data.accessToken) {
|
|
550
|
-
this.tokenManager.saveTokens(
|
|
551
|
-
loginResult.data.accessToken,
|
|
552
|
-
loginResult.data.refreshToken
|
|
553
|
-
);
|
|
554
|
-
LoggerFactory.create("development").log("Tokens saved successfully:", {
|
|
555
|
-
hasAccessToken: !!loginResult.data.accessToken,
|
|
556
|
-
hasRefreshToken: !!loginResult.data.refreshToken,
|
|
557
|
-
domainInfo: this.tokenManager.getDomainInfo()
|
|
558
|
-
});
|
|
559
|
-
const profile = await this.authService.getUserProfile();
|
|
560
|
-
return AuthResultFactory.createSuccess("Login successful", __spreadProps(__spreadValues({}, loginResult.data), {
|
|
561
|
-
profile: profile.data,
|
|
562
|
-
tokenInfo: {
|
|
563
|
-
domain: this.tokenManager.getDomainInfo().domain
|
|
564
|
-
}
|
|
565
|
-
}));
|
|
566
|
-
}
|
|
567
|
-
return AuthResultFactory.createFailure("Authentication failed - no access token received");
|
|
568
|
-
}
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
// src/services/auth/patterns/strategy/login-flow-strategy-factory.ts
|
|
572
|
-
var LoginFlowStrategyFactory = class {
|
|
573
|
-
static createStrategy(action, authService, tokenManager) {
|
|
574
|
-
const strategyFactory = this.strategies.get(action);
|
|
575
|
-
if (!strategyFactory) {
|
|
576
|
-
throw new Error(`No strategy found for action: ${action}`);
|
|
577
|
-
}
|
|
578
|
-
return strategyFactory(authService, tokenManager);
|
|
579
|
-
}
|
|
580
|
-
};
|
|
581
|
-
LoginFlowStrategyFactory.strategies = /* @__PURE__ */ new Map([
|
|
582
|
-
["signup" /* SIGNUP */, (authService, tokenManager) => new SignupFlowStrategy(authService, tokenManager)],
|
|
583
|
-
["login" /* LOGIN */, (authService, tokenManager) => new ExistingUserLoginStrategy(authService, tokenManager)]
|
|
584
|
-
]);
|
|
585
|
-
|
|
586
|
-
// src/services/auth/patterns/state/authenticated-state.ts
|
|
587
|
-
var AuthenticatedState = class {
|
|
588
|
-
async getStatus(tokenManager) {
|
|
589
|
-
const authStatus = await this.buildAuthStatus(tokenManager, true);
|
|
590
|
-
return AuthResultFactory.createSuccess("User is authenticated", authStatus);
|
|
591
|
-
}
|
|
592
|
-
async buildAuthStatus(tokenManager, isAuthenticated) {
|
|
593
|
-
return {
|
|
594
|
-
isAuthenticated,
|
|
595
|
-
hasAccessToken: !!await tokenManager.getAccessToken(),
|
|
596
|
-
hasRefreshToken: !!await tokenManager.getRefreshToken(),
|
|
597
|
-
cookiesSupported: tokenManager.areCookiesSupported(),
|
|
598
|
-
domainInfo: tokenManager.getDomainInfo()
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
// src/services/auth/patterns/state/unauthenticated-state.ts
|
|
604
|
-
var UnauthenticatedState = class {
|
|
605
|
-
async getStatus(tokenManager) {
|
|
606
|
-
const authStatus = await this.buildAuthStatus(tokenManager, false);
|
|
607
|
-
return AuthResultFactory.createFailure("User is not authenticated", authStatus);
|
|
608
|
-
}
|
|
609
|
-
async buildAuthStatus(tokenManager, isAuthenticated) {
|
|
610
|
-
return {
|
|
611
|
-
isAuthenticated,
|
|
612
|
-
hasAccessToken: !!await tokenManager.getAccessToken(),
|
|
613
|
-
hasRefreshToken: !!await tokenManager.getRefreshToken(),
|
|
614
|
-
cookiesSupported: tokenManager.areCookiesSupported(),
|
|
615
|
-
domainInfo: tokenManager.getDomainInfo()
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
// src/services/auth/patterns/state/authentication-status-context.ts
|
|
621
|
-
var AuthenticationStatusContext = class {
|
|
622
|
-
static async getStatus(tokenManager) {
|
|
623
|
-
const isAuthenticated = !!await tokenManager.getAccessToken();
|
|
624
|
-
const state = this.states.get(isAuthenticated) || new UnauthenticatedState();
|
|
625
|
-
return await state.getStatus(tokenManager);
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
AuthenticationStatusContext.states = /* @__PURE__ */ new Map([
|
|
629
|
-
[true, new AuthenticatedState()],
|
|
630
|
-
[false, new UnauthenticatedState()]
|
|
631
|
-
]);
|
|
327
|
+
GET_USER_PROFILE: "/auth/user/profile"};
|
|
632
328
|
|
|
633
329
|
// src/middlewares/handlers/base-middleware-handler.ts
|
|
634
330
|
var BaseMiddlewareHandler = class {
|
|
635
331
|
/**
|
|
636
|
-
* Check if current
|
|
332
|
+
* Check if current route requires authentication
|
|
333
|
+
* Uses inclusive (PROTECTED_ROUTES.INCLUDE) and exclusive (PROTECTED_ROUTES.EXCLUDE) lists
|
|
637
334
|
*/
|
|
638
|
-
|
|
639
|
-
|
|
335
|
+
requiresAuthentication(pathname) {
|
|
336
|
+
const isExcluded = MiddlewareConfig.PROTECTED_ROUTES.EXCLUDE.some(
|
|
337
|
+
(route) => pathname.startsWith(route) || pathname === route
|
|
338
|
+
);
|
|
339
|
+
if (isExcluded) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
return MiddlewareConfig.PROTECTED_ROUTES.INCLUDE.some(
|
|
343
|
+
(route) => pathname.startsWith(route) || pathname === route
|
|
344
|
+
);
|
|
640
345
|
}
|
|
641
346
|
/**
|
|
642
347
|
* Check if user has both access and refresh tokens
|
|
@@ -647,11 +352,12 @@ var BaseMiddlewareHandler = class {
|
|
|
647
352
|
}
|
|
648
353
|
/**
|
|
649
354
|
* Get authentication cookie keys from environment variables
|
|
355
|
+
* Now uses CookieUtils for consistent retrieval
|
|
650
356
|
*/
|
|
651
357
|
getAuthCookieKeys() {
|
|
652
358
|
return {
|
|
653
|
-
accessTokenKey:
|
|
654
|
-
refreshTokenKey:
|
|
359
|
+
accessTokenKey: CookieUtils.getAccessTokenKey(),
|
|
360
|
+
refreshTokenKey: CookieUtils.getRefreshTokenKey()
|
|
655
361
|
};
|
|
656
362
|
}
|
|
657
363
|
/**
|
|
@@ -752,8 +458,8 @@ var MethodFilterHandler = class extends BaseMiddlewareHandler {
|
|
|
752
458
|
// src/middlewares/handlers/authentication-handler.ts
|
|
753
459
|
var AuthenticationHandler = class extends BaseMiddlewareHandler {
|
|
754
460
|
async handle(context) {
|
|
755
|
-
const { cookies,
|
|
756
|
-
if (!this.
|
|
461
|
+
const { cookies, pathname } = context;
|
|
462
|
+
if (!this.requiresAuthentication(pathname)) {
|
|
757
463
|
return this.continue();
|
|
758
464
|
}
|
|
759
465
|
const hasAuthCookies = this.hasAuthenticationCookies(cookies);
|
|
@@ -772,13 +478,10 @@ var AuthenticationHandler = class extends BaseMiddlewareHandler {
|
|
|
772
478
|
return this.allow();
|
|
773
479
|
} catch (error) {
|
|
774
480
|
console.log("JWT validation failed:", error instanceof Error ? error.message : "Unknown error");
|
|
775
|
-
return this.
|
|
481
|
+
return this.addLoginModalParams(context);
|
|
776
482
|
}
|
|
777
483
|
}
|
|
778
484
|
};
|
|
779
|
-
|
|
780
|
-
// src/middlewares/handlers/middleware-chain.ts
|
|
781
|
-
import { NextResponse as NextResponse2 } from "next/server";
|
|
782
485
|
var MiddlewareChain = class {
|
|
783
486
|
constructor() {
|
|
784
487
|
this.handlers = [];
|
|
@@ -794,7 +497,7 @@ var MiddlewareChain = class {
|
|
|
794
497
|
return result;
|
|
795
498
|
}
|
|
796
499
|
}
|
|
797
|
-
return
|
|
500
|
+
return NextResponse.next();
|
|
798
501
|
}
|
|
799
502
|
};
|
|
800
503
|
|
|
@@ -813,8 +516,5 @@ async function middleware(req) {
|
|
|
813
516
|
const chain = new MiddlewareChain().addHandler(new MethodFilterHandler()).addHandler(new AuthenticationHandler());
|
|
814
517
|
return await chain.process(context);
|
|
815
518
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
middleware,
|
|
819
|
-
middlewareMatcher
|
|
820
|
-
};
|
|
519
|
+
|
|
520
|
+
export { config, middleware, middlewareMatcher };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie utility for managing authentication tokens with automatic SSR and client-side support
|
|
3
|
+
* Automatically chooses the right method based on environment
|
|
4
|
+
*/
|
|
5
|
+
declare class CookieUtils {
|
|
6
|
+
/**
|
|
7
|
+
* Get the access token cookie key
|
|
8
|
+
*/
|
|
9
|
+
static getAccessTokenKey(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Get the refresh token cookie key
|
|
12
|
+
*/
|
|
13
|
+
static getRefreshTokenKey(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get root domain for subdomain support
|
|
16
|
+
* Must match the domain used by backend when setting cookies
|
|
17
|
+
*/
|
|
18
|
+
private static getRootDomain;
|
|
19
|
+
private static readonly COOKIE_DOMAIN;
|
|
20
|
+
/**
|
|
21
|
+
* Get common cookie options
|
|
22
|
+
*/
|
|
23
|
+
private static getCookieOptions;
|
|
24
|
+
/**
|
|
25
|
+
* Check if running on client side
|
|
26
|
+
*/
|
|
27
|
+
private static isClientSide;
|
|
28
|
+
/**
|
|
29
|
+
* Check if running on server side
|
|
30
|
+
*/
|
|
31
|
+
private static isServerSide;
|
|
32
|
+
/**
|
|
33
|
+
* Dynamically import server action for server-side operations
|
|
34
|
+
*/
|
|
35
|
+
private static getServerTokens;
|
|
36
|
+
/**
|
|
37
|
+
* Set access token in cookie (client-side only)
|
|
38
|
+
*/
|
|
39
|
+
static setAccessToken(token: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Set refresh token in cookie (client-side only)
|
|
42
|
+
*/
|
|
43
|
+
static setRefreshToken(token: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get access token - automatically handles client/server side
|
|
46
|
+
*/
|
|
47
|
+
static getAccessToken(): Promise<string | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Get refresh token - automatically handles client/server side
|
|
50
|
+
*/
|
|
51
|
+
static getRefreshToken(): Promise<string | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Get both tokens - automatically handles client/server side
|
|
54
|
+
*/
|
|
55
|
+
static getTokens(): Promise<{
|
|
56
|
+
accessToken: string | null;
|
|
57
|
+
refreshToken: string | null;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Clear all authentication cookies (client-side only)
|
|
61
|
+
* Clears cookies from both current domain and root domain
|
|
62
|
+
*/
|
|
63
|
+
static clearAuthCookies(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Check if cookies are supported/enabled (client-side only)
|
|
66
|
+
*/
|
|
67
|
+
static areCookiesEnabled(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Get all authentication cookies - automatically handles client/server side
|
|
70
|
+
*/
|
|
71
|
+
static getAllAuthCookies(): Promise<Record<string, string>>;
|
|
72
|
+
/**
|
|
73
|
+
* Set multiple tokens at once (client-side only)
|
|
74
|
+
*/
|
|
75
|
+
static setTokens(accessToken: string, refreshToken: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Check if user has valid tokens - automatically handles client/server side
|
|
78
|
+
*/
|
|
79
|
+
static hasValidTokens(): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* Get current domain information for debugging
|
|
82
|
+
*/
|
|
83
|
+
static getDomainInfo(): Record<string, string>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { CookieUtils };
|