mailsentry-auth 0.1.1 → 0.1.4
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 +67 -44
- package/dist/index.d.ts +67 -44
- package/dist/index.js +121 -46
- package/dist/index.mjs +120 -45
- package/dist/middleware.d.mts +7 -12
- package/dist/middleware.mjs +120 -103
- package/dist/utils/cookie-utils.js +28 -9
- package/dist/utils/cookie-utils.mjs +28 -9
- package/package.json +20 -18
package/dist/middleware.mjs
CHANGED
|
@@ -23,13 +23,32 @@ import { NextResponse } from "next/server";
|
|
|
23
23
|
|
|
24
24
|
// src/config/middleware.ts
|
|
25
25
|
var MiddlewareConfig = class {
|
|
26
|
+
/**
|
|
27
|
+
* Get the base domain from environment or use default
|
|
28
|
+
*/
|
|
29
|
+
static getBaseDomain() {
|
|
30
|
+
return process.env.NEXT_PUBLIC_BASE_DOMAIN || "cutly.io";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the protocol based on environment
|
|
34
|
+
*/
|
|
35
|
+
static getProtocol() {
|
|
36
|
+
return process.env.NODE_ENV === "production" ? "https" : "http";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the dashboard subdomain URL
|
|
40
|
+
*/
|
|
41
|
+
static getDashboardUrl(path = "") {
|
|
42
|
+
return `${this.getProtocol()}://${this.SUBDOMAINS.DASHBOARD}.${this.getBaseDomain()}${path}`;
|
|
43
|
+
}
|
|
26
44
|
};
|
|
27
|
-
//
|
|
28
|
-
MiddlewareConfig.
|
|
29
|
-
DASHBOARD: "
|
|
45
|
+
// Subdomain configuration
|
|
46
|
+
MiddlewareConfig.SUBDOMAINS = {
|
|
47
|
+
DASHBOARD: "dashboard"
|
|
30
48
|
};
|
|
31
|
-
//
|
|
32
|
-
MiddlewareConfig.
|
|
49
|
+
// Routes
|
|
50
|
+
MiddlewareConfig.ROUTES = {
|
|
51
|
+
ROOT: "/",
|
|
33
52
|
LOGIN: "/login"
|
|
34
53
|
};
|
|
35
54
|
// HTTP methods to process
|
|
@@ -37,7 +56,8 @@ MiddlewareConfig.ALLOWED_METHODS = ["GET", "HEAD"];
|
|
|
37
56
|
// Query parameters
|
|
38
57
|
MiddlewareConfig.QUERY_PARAMS = {
|
|
39
58
|
LOGIN_REQUIRED: "sign_in_required",
|
|
40
|
-
AUTH_CHECKED: "auth_checked"
|
|
59
|
+
AUTH_CHECKED: "auth_checked",
|
|
60
|
+
REDIRECT_URL: "redirect_url"
|
|
41
61
|
};
|
|
42
62
|
// Query parameter values
|
|
43
63
|
MiddlewareConfig.QUERY_VALUES = {
|
|
@@ -45,8 +65,7 @@ MiddlewareConfig.QUERY_VALUES = {
|
|
|
45
65
|
AUTH_CHECKED: "1"
|
|
46
66
|
};
|
|
47
67
|
var middlewareMatcher = [
|
|
48
|
-
"/((?!api|_next/static|_next/image|_next/webpack-hmr|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js)$).*)"
|
|
49
|
-
"/login"
|
|
68
|
+
"/((?!api|_next/static|_next/image|_next/webpack-hmr|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js)$).*)"
|
|
50
69
|
];
|
|
51
70
|
var config = {
|
|
52
71
|
matcher: middlewareMatcher
|
|
@@ -67,12 +86,18 @@ var _CookieUtils = class _CookieUtils {
|
|
|
67
86
|
static getRefreshTokenKey() {
|
|
68
87
|
return process.env.AUTH_REFRESH_TOKEN_KEY || "auth_refresh_token";
|
|
69
88
|
}
|
|
70
|
-
// Use current domain in development
|
|
71
89
|
/**
|
|
72
90
|
* Get root domain for subdomain support
|
|
91
|
+
* Must match the domain used by backend when setting cookies
|
|
73
92
|
*/
|
|
74
93
|
static getRootDomain() {
|
|
75
|
-
if (typeof window === "undefined")
|
|
94
|
+
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
|
+
}
|
|
76
101
|
const hostname = window.location.hostname;
|
|
77
102
|
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
78
103
|
return void 0;
|
|
@@ -86,13 +111,15 @@ var _CookieUtils = class _CookieUtils {
|
|
|
86
111
|
}
|
|
87
112
|
return void 0;
|
|
88
113
|
}
|
|
114
|
+
// Use current domain in development
|
|
89
115
|
/**
|
|
90
116
|
* Get common cookie options
|
|
91
117
|
*/
|
|
92
118
|
static getCookieOptions() {
|
|
93
119
|
return {
|
|
94
120
|
path: "/",
|
|
95
|
-
sameSite: "
|
|
121
|
+
sameSite: "lax",
|
|
122
|
+
// Changed from 'strict' to 'lax' for cross-subdomain
|
|
96
123
|
secure: process.env.NODE_ENV === "production",
|
|
97
124
|
// Only secure in production
|
|
98
125
|
domain: this.COOKIE_DOMAIN
|
|
@@ -182,18 +209,26 @@ var _CookieUtils = class _CookieUtils {
|
|
|
182
209
|
}
|
|
183
210
|
/**
|
|
184
211
|
* Clear all authentication cookies (client-side only)
|
|
212
|
+
* Clears cookies from both current domain and root domain
|
|
185
213
|
*/
|
|
186
214
|
static clearAuthCookies() {
|
|
187
215
|
if (this.isServerSide()) {
|
|
188
216
|
console.warn("clearAuthCookies called on server side - use server actions instead");
|
|
189
217
|
return;
|
|
190
218
|
}
|
|
191
|
-
const
|
|
219
|
+
const accessKey = this.getAccessTokenKey();
|
|
220
|
+
const refreshKey = this.getRefreshTokenKey();
|
|
221
|
+
const rootDomainOptions = {
|
|
192
222
|
path: "/",
|
|
193
223
|
domain: this.COOKIE_DOMAIN
|
|
194
224
|
};
|
|
195
|
-
Cookies.remove(
|
|
196
|
-
Cookies.remove(
|
|
225
|
+
Cookies.remove(accessKey, rootDomainOptions);
|
|
226
|
+
Cookies.remove(refreshKey, rootDomainOptions);
|
|
227
|
+
const currentDomainOptions = {
|
|
228
|
+
path: "/"
|
|
229
|
+
};
|
|
230
|
+
Cookies.remove(accessKey, currentDomainOptions);
|
|
231
|
+
Cookies.remove(refreshKey, currentDomainOptions);
|
|
197
232
|
}
|
|
198
233
|
/**
|
|
199
234
|
* Check if cookies are supported/enabled (client-side only)
|
|
@@ -260,18 +295,21 @@ var _CookieUtils = class _CookieUtils {
|
|
|
260
295
|
return {
|
|
261
296
|
error: "Server-side rendering - no domain info available",
|
|
262
297
|
environment: process.env.NODE_ENV || "unknown",
|
|
298
|
+
cookieDomain: this.COOKIE_DOMAIN || "undefined",
|
|
299
|
+
baseDomain: process.env.NEXT_PUBLIC_BASE_DOMAIN || "undefined",
|
|
263
300
|
recommendation: "Use server actions for server-side cookie access"
|
|
264
301
|
};
|
|
265
302
|
}
|
|
266
303
|
return {
|
|
267
304
|
hostname: window.location.hostname,
|
|
268
|
-
|
|
305
|
+
cookieDomain: this.COOKIE_DOMAIN || "current domain",
|
|
269
306
|
environment: process.env.NODE_ENV || "unknown",
|
|
270
|
-
protocol: window.location.protocol
|
|
307
|
+
protocol: window.location.protocol,
|
|
308
|
+
sameSite: "lax"
|
|
271
309
|
};
|
|
272
310
|
}
|
|
273
311
|
};
|
|
274
|
-
// Domain configuration
|
|
312
|
+
// Domain configuration - computed once
|
|
275
313
|
_CookieUtils.COOKIE_DOMAIN = process.env.NODE_ENV === "production" ? _CookieUtils.getRootDomain() : void 0;
|
|
276
314
|
var CookieUtils = _CookieUtils;
|
|
277
315
|
|
|
@@ -356,6 +394,33 @@ LocalStorageUtils.USER_PROFILE_STORAGE_KEY = "user_profile_data";
|
|
|
356
394
|
LocalStorageUtils.USER_PROFILE_TIMESTAMP_KEY = "user_profile_timestamp";
|
|
357
395
|
LocalStorageUtils.DEFAULT_CACHE_DURATION = 5 * 60 * 1e3;
|
|
358
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
|
+
|
|
359
424
|
// src/services/api/endpoints.ts
|
|
360
425
|
var AUTH_ENDPOINTS = {
|
|
361
426
|
// Email existence check
|
|
@@ -567,6 +632,12 @@ AuthenticationStatusContext.states = /* @__PURE__ */ new Map([
|
|
|
567
632
|
|
|
568
633
|
// src/middlewares/handlers/base-middleware-handler.ts
|
|
569
634
|
var BaseMiddlewareHandler = class {
|
|
635
|
+
/**
|
|
636
|
+
* Check if current request is on dashboard subdomain
|
|
637
|
+
*/
|
|
638
|
+
isDashboardSubdomain(hostname) {
|
|
639
|
+
return UrlUtils.getSubdomain(hostname) === MiddlewareConfig.SUBDOMAINS.DASHBOARD;
|
|
640
|
+
}
|
|
570
641
|
/**
|
|
571
642
|
* Check if user has both access and refresh tokens
|
|
572
643
|
*/
|
|
@@ -575,7 +646,7 @@ var BaseMiddlewareHandler = class {
|
|
|
575
646
|
return cookies.has(accessTokenKey) && cookies.has(refreshTokenKey);
|
|
576
647
|
}
|
|
577
648
|
/**
|
|
578
|
-
* Get authentication cookie keys from environment variables
|
|
649
|
+
* Get authentication cookie keys from environment variables
|
|
579
650
|
*/
|
|
580
651
|
getAuthCookieKeys() {
|
|
581
652
|
return {
|
|
@@ -584,49 +655,21 @@ var BaseMiddlewareHandler = class {
|
|
|
584
655
|
};
|
|
585
656
|
}
|
|
586
657
|
/**
|
|
587
|
-
*
|
|
658
|
+
* Add login modal parameters to current URL
|
|
659
|
+
* Redirects to same page with sign_in_required parameter to trigger modal
|
|
588
660
|
*/
|
|
589
|
-
|
|
590
|
-
|
|
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) {
|
|
661
|
+
addLoginModalParams(context) {
|
|
662
|
+
if (this.hasAuthCheckedParam(context.searchParams)) {
|
|
600
663
|
return NextResponse.next();
|
|
601
664
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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;
|
|
665
|
+
const currentUrl = new URL(context.request.url);
|
|
666
|
+
currentUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED);
|
|
667
|
+
currentUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
668
|
+
currentUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.REDIRECT_URL);
|
|
669
|
+
currentUrl.searchParams.set(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED, MiddlewareConfig.QUERY_VALUES.LOGIN_REQUIRED);
|
|
670
|
+
currentUrl.searchParams.set(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED, MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED);
|
|
671
|
+
currentUrl.searchParams.set(MiddlewareConfig.QUERY_PARAMS.REDIRECT_URL, context.pathname);
|
|
672
|
+
return NextResponse.redirect(currentUrl);
|
|
630
673
|
}
|
|
631
674
|
/**
|
|
632
675
|
* Check if request method is allowed
|
|
@@ -653,24 +696,25 @@ var BaseMiddlewareHandler = class {
|
|
|
653
696
|
return NextResponse.next();
|
|
654
697
|
}
|
|
655
698
|
/**
|
|
656
|
-
*
|
|
699
|
+
* Remove auth-related query parameters and redirect to clean URL
|
|
657
700
|
*/
|
|
658
|
-
|
|
701
|
+
removeAuthParams(context) {
|
|
659
702
|
const cleanUrl = new URL(context.nextUrl.toString());
|
|
660
703
|
cleanUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED);
|
|
661
704
|
cleanUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
662
|
-
|
|
705
|
+
cleanUrl.searchParams.delete(MiddlewareConfig.QUERY_PARAMS.REDIRECT_URL);
|
|
706
|
+
return NextResponse.redirect(cleanUrl, { status: 302 });
|
|
663
707
|
}
|
|
664
708
|
/**
|
|
665
709
|
* Check if URL has auth-related query parameters that need cleanup
|
|
666
710
|
*/
|
|
667
711
|
hasAuthQueryParams(searchParams) {
|
|
668
|
-
return searchParams.has(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED) || searchParams.has(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
712
|
+
return searchParams.has(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED) || searchParams.has(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED) || searchParams.has(MiddlewareConfig.QUERY_PARAMS.REDIRECT_URL);
|
|
669
713
|
}
|
|
670
714
|
/**
|
|
671
|
-
*
|
|
715
|
+
* Validate user authentication by calling getUserProfile API
|
|
672
716
|
*/
|
|
673
|
-
async
|
|
717
|
+
async validateUserAuth(cookies) {
|
|
674
718
|
var _a;
|
|
675
719
|
const { accessTokenKey } = this.getAuthCookieKeys();
|
|
676
720
|
const accessToken = (_a = cookies.get(accessTokenKey)) == null ? void 0 : _a.value;
|
|
@@ -705,63 +749,34 @@ var MethodFilterHandler = class extends BaseMiddlewareHandler {
|
|
|
705
749
|
}
|
|
706
750
|
};
|
|
707
751
|
|
|
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
752
|
// src/middlewares/handlers/authentication-handler.ts
|
|
720
753
|
var AuthenticationHandler = class extends BaseMiddlewareHandler {
|
|
721
754
|
async handle(context) {
|
|
722
|
-
const { cookies,
|
|
755
|
+
const { cookies, hostname } = context;
|
|
756
|
+
if (!this.isDashboardSubdomain(hostname)) {
|
|
757
|
+
return this.continue();
|
|
758
|
+
}
|
|
723
759
|
const hasAuthCookies = this.hasAuthenticationCookies(cookies);
|
|
724
760
|
if (!hasAuthCookies) {
|
|
725
|
-
|
|
726
|
-
return this.redirectWithAuthParams(nextUrl);
|
|
727
|
-
}
|
|
728
|
-
return this.continue();
|
|
761
|
+
return this.addLoginModalParams(context);
|
|
729
762
|
}
|
|
730
763
|
try {
|
|
731
|
-
const response = await this.
|
|
764
|
+
const response = await this.validateUserAuth(cookies);
|
|
732
765
|
if (!response.ok) {
|
|
733
766
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
734
767
|
}
|
|
735
768
|
await response.json();
|
|
736
769
|
if (this.hasAuthQueryParams(context.searchParams)) {
|
|
737
|
-
return this.
|
|
770
|
+
return this.removeAuthParams(context);
|
|
738
771
|
}
|
|
739
772
|
return this.allow();
|
|
740
773
|
} catch (error) {
|
|
741
774
|
console.log("JWT validation failed:", error instanceof Error ? error.message : "Unknown error");
|
|
742
|
-
|
|
743
|
-
return this.redirectWithAuthParams(nextUrl);
|
|
744
|
-
}
|
|
745
|
-
return this.continue();
|
|
775
|
+
return this.addLoginModalParams(context);
|
|
746
776
|
}
|
|
747
777
|
}
|
|
748
778
|
};
|
|
749
779
|
|
|
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
780
|
// src/middlewares/handlers/middleware-chain.ts
|
|
766
781
|
import { NextResponse as NextResponse2 } from "next/server";
|
|
767
782
|
var MiddlewareChain = class {
|
|
@@ -785,15 +800,17 @@ var MiddlewareChain = class {
|
|
|
785
800
|
|
|
786
801
|
// src/middleware.ts
|
|
787
802
|
async function middleware(req) {
|
|
803
|
+
const hostname = req.headers.get("host") || req.nextUrl.hostname;
|
|
788
804
|
const context = {
|
|
789
805
|
request: req,
|
|
790
806
|
method: req.method,
|
|
791
807
|
pathname: req.nextUrl.pathname,
|
|
792
808
|
searchParams: req.nextUrl.searchParams,
|
|
793
809
|
cookies: req.cookies,
|
|
794
|
-
nextUrl: req.nextUrl
|
|
810
|
+
nextUrl: req.nextUrl,
|
|
811
|
+
hostname
|
|
795
812
|
};
|
|
796
|
-
const chain = new MiddlewareChain().addHandler(new MethodFilterHandler()).addHandler(new
|
|
813
|
+
const chain = new MiddlewareChain().addHandler(new MethodFilterHandler()).addHandler(new AuthenticationHandler());
|
|
797
814
|
return await chain.process(context);
|
|
798
815
|
}
|
|
799
816
|
export {
|
|
@@ -13,12 +13,18 @@ var _CookieUtils = class _CookieUtils {
|
|
|
13
13
|
static getRefreshTokenKey() {
|
|
14
14
|
return process.env.AUTH_REFRESH_TOKEN_KEY || "auth_refresh_token";
|
|
15
15
|
}
|
|
16
|
-
// Use current domain in development
|
|
17
16
|
/**
|
|
18
17
|
* Get root domain for subdomain support
|
|
18
|
+
* Must match the domain used by backend when setting cookies
|
|
19
19
|
*/
|
|
20
20
|
static getRootDomain() {
|
|
21
|
-
if (typeof window === "undefined")
|
|
21
|
+
if (typeof window === "undefined") {
|
|
22
|
+
const baseDomain = process.env.NEXT_PUBLIC_BASE_DOMAIN;
|
|
23
|
+
if (baseDomain && process.env.NODE_ENV === "production") {
|
|
24
|
+
return `.${baseDomain}`;
|
|
25
|
+
}
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
22
28
|
const hostname = window.location.hostname;
|
|
23
29
|
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
24
30
|
return void 0;
|
|
@@ -32,13 +38,15 @@ var _CookieUtils = class _CookieUtils {
|
|
|
32
38
|
}
|
|
33
39
|
return void 0;
|
|
34
40
|
}
|
|
41
|
+
// Use current domain in development
|
|
35
42
|
/**
|
|
36
43
|
* Get common cookie options
|
|
37
44
|
*/
|
|
38
45
|
static getCookieOptions() {
|
|
39
46
|
return {
|
|
40
47
|
path: "/",
|
|
41
|
-
sameSite: "
|
|
48
|
+
sameSite: "lax",
|
|
49
|
+
// Changed from 'strict' to 'lax' for cross-subdomain
|
|
42
50
|
secure: process.env.NODE_ENV === "production",
|
|
43
51
|
// Only secure in production
|
|
44
52
|
domain: this.COOKIE_DOMAIN
|
|
@@ -128,18 +136,26 @@ var _CookieUtils = class _CookieUtils {
|
|
|
128
136
|
}
|
|
129
137
|
/**
|
|
130
138
|
* Clear all authentication cookies (client-side only)
|
|
139
|
+
* Clears cookies from both current domain and root domain
|
|
131
140
|
*/
|
|
132
141
|
static clearAuthCookies() {
|
|
133
142
|
if (this.isServerSide()) {
|
|
134
143
|
console.warn("clearAuthCookies called on server side - use server actions instead");
|
|
135
144
|
return;
|
|
136
145
|
}
|
|
137
|
-
const
|
|
146
|
+
const accessKey = this.getAccessTokenKey();
|
|
147
|
+
const refreshKey = this.getRefreshTokenKey();
|
|
148
|
+
const rootDomainOptions = {
|
|
138
149
|
path: "/",
|
|
139
150
|
domain: this.COOKIE_DOMAIN
|
|
140
151
|
};
|
|
141
|
-
_jscookie2.default.remove(
|
|
142
|
-
_jscookie2.default.remove(
|
|
152
|
+
_jscookie2.default.remove(accessKey, rootDomainOptions);
|
|
153
|
+
_jscookie2.default.remove(refreshKey, rootDomainOptions);
|
|
154
|
+
const currentDomainOptions = {
|
|
155
|
+
path: "/"
|
|
156
|
+
};
|
|
157
|
+
_jscookie2.default.remove(accessKey, currentDomainOptions);
|
|
158
|
+
_jscookie2.default.remove(refreshKey, currentDomainOptions);
|
|
143
159
|
}
|
|
144
160
|
/**
|
|
145
161
|
* Check if cookies are supported/enabled (client-side only)
|
|
@@ -206,18 +222,21 @@ var _CookieUtils = class _CookieUtils {
|
|
|
206
222
|
return {
|
|
207
223
|
error: "Server-side rendering - no domain info available",
|
|
208
224
|
environment: process.env.NODE_ENV || "unknown",
|
|
225
|
+
cookieDomain: this.COOKIE_DOMAIN || "undefined",
|
|
226
|
+
baseDomain: process.env.NEXT_PUBLIC_BASE_DOMAIN || "undefined",
|
|
209
227
|
recommendation: "Use server actions for server-side cookie access"
|
|
210
228
|
};
|
|
211
229
|
}
|
|
212
230
|
return {
|
|
213
231
|
hostname: window.location.hostname,
|
|
214
|
-
|
|
232
|
+
cookieDomain: this.COOKIE_DOMAIN || "current domain",
|
|
215
233
|
environment: process.env.NODE_ENV || "unknown",
|
|
216
|
-
protocol: window.location.protocol
|
|
234
|
+
protocol: window.location.protocol,
|
|
235
|
+
sameSite: "lax"
|
|
217
236
|
};
|
|
218
237
|
}
|
|
219
238
|
};
|
|
220
|
-
// Domain configuration
|
|
239
|
+
// Domain configuration - computed once
|
|
221
240
|
_CookieUtils.COOKIE_DOMAIN = process.env.NODE_ENV === "production" ? _CookieUtils.getRootDomain() : void 0;
|
|
222
241
|
var CookieUtils = _CookieUtils;
|
|
223
242
|
|
|
@@ -13,12 +13,18 @@ var _CookieUtils = class _CookieUtils {
|
|
|
13
13
|
static getRefreshTokenKey() {
|
|
14
14
|
return process.env.AUTH_REFRESH_TOKEN_KEY || "auth_refresh_token";
|
|
15
15
|
}
|
|
16
|
-
// Use current domain in development
|
|
17
16
|
/**
|
|
18
17
|
* Get root domain for subdomain support
|
|
18
|
+
* Must match the domain used by backend when setting cookies
|
|
19
19
|
*/
|
|
20
20
|
static getRootDomain() {
|
|
21
|
-
if (typeof window === "undefined")
|
|
21
|
+
if (typeof window === "undefined") {
|
|
22
|
+
const baseDomain = process.env.NEXT_PUBLIC_BASE_DOMAIN;
|
|
23
|
+
if (baseDomain && process.env.NODE_ENV === "production") {
|
|
24
|
+
return `.${baseDomain}`;
|
|
25
|
+
}
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
22
28
|
const hostname = window.location.hostname;
|
|
23
29
|
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
24
30
|
return void 0;
|
|
@@ -32,13 +38,15 @@ var _CookieUtils = class _CookieUtils {
|
|
|
32
38
|
}
|
|
33
39
|
return void 0;
|
|
34
40
|
}
|
|
41
|
+
// Use current domain in development
|
|
35
42
|
/**
|
|
36
43
|
* Get common cookie options
|
|
37
44
|
*/
|
|
38
45
|
static getCookieOptions() {
|
|
39
46
|
return {
|
|
40
47
|
path: "/",
|
|
41
|
-
sameSite: "
|
|
48
|
+
sameSite: "lax",
|
|
49
|
+
// Changed from 'strict' to 'lax' for cross-subdomain
|
|
42
50
|
secure: process.env.NODE_ENV === "production",
|
|
43
51
|
// Only secure in production
|
|
44
52
|
domain: this.COOKIE_DOMAIN
|
|
@@ -128,18 +136,26 @@ var _CookieUtils = class _CookieUtils {
|
|
|
128
136
|
}
|
|
129
137
|
/**
|
|
130
138
|
* Clear all authentication cookies (client-side only)
|
|
139
|
+
* Clears cookies from both current domain and root domain
|
|
131
140
|
*/
|
|
132
141
|
static clearAuthCookies() {
|
|
133
142
|
if (this.isServerSide()) {
|
|
134
143
|
console.warn("clearAuthCookies called on server side - use server actions instead");
|
|
135
144
|
return;
|
|
136
145
|
}
|
|
137
|
-
const
|
|
146
|
+
const accessKey = this.getAccessTokenKey();
|
|
147
|
+
const refreshKey = this.getRefreshTokenKey();
|
|
148
|
+
const rootDomainOptions = {
|
|
138
149
|
path: "/",
|
|
139
150
|
domain: this.COOKIE_DOMAIN
|
|
140
151
|
};
|
|
141
|
-
Cookies.remove(
|
|
142
|
-
Cookies.remove(
|
|
152
|
+
Cookies.remove(accessKey, rootDomainOptions);
|
|
153
|
+
Cookies.remove(refreshKey, rootDomainOptions);
|
|
154
|
+
const currentDomainOptions = {
|
|
155
|
+
path: "/"
|
|
156
|
+
};
|
|
157
|
+
Cookies.remove(accessKey, currentDomainOptions);
|
|
158
|
+
Cookies.remove(refreshKey, currentDomainOptions);
|
|
143
159
|
}
|
|
144
160
|
/**
|
|
145
161
|
* Check if cookies are supported/enabled (client-side only)
|
|
@@ -206,18 +222,21 @@ var _CookieUtils = class _CookieUtils {
|
|
|
206
222
|
return {
|
|
207
223
|
error: "Server-side rendering - no domain info available",
|
|
208
224
|
environment: process.env.NODE_ENV || "unknown",
|
|
225
|
+
cookieDomain: this.COOKIE_DOMAIN || "undefined",
|
|
226
|
+
baseDomain: process.env.NEXT_PUBLIC_BASE_DOMAIN || "undefined",
|
|
209
227
|
recommendation: "Use server actions for server-side cookie access"
|
|
210
228
|
};
|
|
211
229
|
}
|
|
212
230
|
return {
|
|
213
231
|
hostname: window.location.hostname,
|
|
214
|
-
|
|
232
|
+
cookieDomain: this.COOKIE_DOMAIN || "current domain",
|
|
215
233
|
environment: process.env.NODE_ENV || "unknown",
|
|
216
|
-
protocol: window.location.protocol
|
|
234
|
+
protocol: window.location.protocol,
|
|
235
|
+
sameSite: "lax"
|
|
217
236
|
};
|
|
218
237
|
}
|
|
219
238
|
};
|
|
220
|
-
// Domain configuration
|
|
239
|
+
// Domain configuration - computed once
|
|
221
240
|
_CookieUtils.COOKIE_DOMAIN = process.env.NODE_ENV === "production" ? _CookieUtils.getRootDomain() : void 0;
|
|
222
241
|
var CookieUtils = _CookieUtils;
|
|
223
242
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailsentry-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Next.js 15 authentication package with multi-step auth flow, cross-tab sync, and Zustand state management",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -38,21 +38,6 @@
|
|
|
38
38
|
"README.md",
|
|
39
39
|
"ZUSTAND_MIGRATION.md"
|
|
40
40
|
],
|
|
41
|
-
"scripts": {
|
|
42
|
-
"dev": "next dev --turbopack",
|
|
43
|
-
"build": "next build",
|
|
44
|
-
"build:package": "tsup",
|
|
45
|
-
"build:package:watch": "tsup --watch",
|
|
46
|
-
"build:link": "npm run build:package && npm link",
|
|
47
|
-
"link:sample": "cd ../nextjs-sample && npm link mailsentry-auth",
|
|
48
|
-
"build:link:sample": "npm run build:link && npm run link:sample",
|
|
49
|
-
"link:cutly": "cd ../cutly && pnpm link ../auth-nextjs",
|
|
50
|
-
"build:link:cutly": "pnpm run build:package && pnpm run link:cutly",
|
|
51
|
-
"start": "next start",
|
|
52
|
-
"lint": "next lint",
|
|
53
|
-
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,scss}\"",
|
|
54
|
-
"prepublishOnly": "npm run build:package"
|
|
55
|
-
},
|
|
56
41
|
"peerDependencies": {
|
|
57
42
|
"next": ">=15.0.0",
|
|
58
43
|
"react": ">=18.0.0 || >=19.0.0",
|
|
@@ -99,5 +84,22 @@
|
|
|
99
84
|
"url": "https://github.com/danielaei/mailsentry-auth.git"
|
|
100
85
|
},
|
|
101
86
|
"author": "danielaei",
|
|
102
|
-
"license": "MIT"
|
|
103
|
-
|
|
87
|
+
"license": "MIT",
|
|
88
|
+
"scripts": {
|
|
89
|
+
"dev": "next dev --turbopack",
|
|
90
|
+
"build": "next build",
|
|
91
|
+
"build:package": "tsup",
|
|
92
|
+
"build:package:watch": "tsup --watch",
|
|
93
|
+
"build:link": "npm run build:package && npm link",
|
|
94
|
+
"link:sample": "cd ../nextjs-sample && npm link mailsentry-auth",
|
|
95
|
+
"build:link:sample": "npm run build:link && npm run link:sample",
|
|
96
|
+
"link:cutly": "cd ../cutly && pnpm link ../auth-nextjs",
|
|
97
|
+
"build:link:cutly": "pnpm run build:package && pnpm run link:cutly",
|
|
98
|
+
"start": "next start",
|
|
99
|
+
"lint": "next lint",
|
|
100
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,scss}\"",
|
|
101
|
+
"publish:patch": "bash scripts/publish-package.sh patch",
|
|
102
|
+
"publish:minor": "bash scripts/publish-package.sh minor",
|
|
103
|
+
"publish:major": "bash scripts/publish-package.sh major"
|
|
104
|
+
}
|
|
105
|
+
}
|