@neondatabase/auth 0.2.0-beta.1 → 0.4.0-beta
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/LICENSE +201 -0
- package/README.md +22 -7
- package/codemods/migrate-auth-ui-imports.mjs +439 -0
- package/dist/{adapter-core-CnrOXh1T.d.mts → adapter-core-BWM7cWOp.d.mts} +465 -401
- package/dist/{adapter-core-CtmnMMJ7.mjs → adapter-core-Bt4M5I2g.mjs} +21 -11
- package/dist/auth-interface-Clz-oWq1.d.mts +8 -0
- package/dist/better-auth-helpers-Bkezghej.mjs +541 -0
- package/dist/{better-auth-react-adapter-DNi5PC5D.d.mts → better-auth-react-adapter-BDxJ65mF.d.mts} +389 -302
- package/dist/{better-auth-react-adapter-Dv-o6A6O.mjs → better-auth-react-adapter-aMv8WeDb.mjs} +1 -1
- package/dist/{index-CzsGMS7C.d.mts → index-DHryUj7e.d.mts} +0 -1
- package/dist/index.d.mts +94 -5
- package/dist/index.mjs +4 -3
- package/dist/{neon-auth-Cs2cWh1B.mjs → neon-auth-CS4FpK2X.mjs} +1 -1
- package/dist/next/index.d.mts +161 -73
- package/dist/next/index.mjs +5 -4
- package/dist/next/server/index.d.mts +25 -5
- package/dist/next/server/index.mjs +369 -251
- package/dist/react/adapters/index.d.mts +3 -3
- package/dist/react/adapters/index.mjs +2 -2
- package/dist/react/index.d.mts +6 -5
- package/dist/react/index.mjs +5 -5
- package/dist/react/ui/index.d.mts +1 -2
- package/dist/react/ui/index.mjs +2 -4
- package/dist/react/ui/server.mjs +2 -2
- package/dist/{supabase-adapter-DUqw2fw8.d.mts → supabase-adapter-BGwV0Vu2.d.mts} +429 -373
- package/dist/{supabase-adapter-BlcGPyOf.mjs → supabase-adapter-DBt4LJJd.mjs} +3 -514
- package/dist/types/index.d.mts +2 -2
- package/dist/ui/.safelist.html +1 -1
- package/dist/ui/css.css +2 -2
- package/dist/ui/theme.css +1 -1
- package/dist/ui-CnVnqGns.mjs +3 -0
- package/dist/vanilla/adapters/index.d.mts +4 -3
- package/dist/vanilla/adapters/index.mjs +2 -2
- package/dist/vanilla/index.d.mts +4 -3
- package/dist/vanilla/index.mjs +2 -2
- package/llms.txt +2 -2
- package/package.json +27 -21
- package/dist/chunk-VCZJYX65-CLnrj1o7-D6ZQkcc_.mjs +0 -543
- package/dist/constants-Cupc_bln.mjs +0 -28
- package/dist/neon-auth-BEGCfAe6.d.mts +0 -107
- package/dist/ui-COLWzDsu.mjs +0 -12469
- /package/dist/{adapters-B7YKkjaL.mjs → adapters-CUvhsAvY.mjs} +0 -0
- /package/dist/{index-CPnFzULh.d.mts → index-B0Pd4HOH.d.mts} +0 -0
- /package/dist/{index-OEBbnNdr.d.mts → index-CzpoWrv9.d.mts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as isAuthApiError, l as isAuthError, m as NEON_AUTH_SESSION_VERIFIER_PARAM_NAME, o as AuthApiError, r as normalizeBetterAuthError, s as AuthError } from "../../better-auth-helpers-Bkezghej.mjs";
|
|
2
2
|
import { SignJWT, jwtVerify } from "jose";
|
|
3
3
|
import { parseCookies, parseSetCookieHeader } from "better-auth/cookies";
|
|
4
4
|
import { cookies, headers } from "next/headers";
|
|
@@ -54,6 +54,10 @@ const API_ENDPOINTS = {
|
|
|
54
54
|
emailOtp: {
|
|
55
55
|
path: "sign-in/email-otp",
|
|
56
56
|
method: "POST"
|
|
57
|
+
},
|
|
58
|
+
magicLink: {
|
|
59
|
+
path: "sign-in/magic-link",
|
|
60
|
+
method: "POST"
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
signUp: { email: {
|
|
@@ -273,7 +277,11 @@ const API_ENDPOINTS = {
|
|
|
273
277
|
path: "email-otp/passcode",
|
|
274
278
|
method: "POST"
|
|
275
279
|
}
|
|
276
|
-
}
|
|
280
|
+
},
|
|
281
|
+
magicLink: { verify: {
|
|
282
|
+
path: "magic-link/verify",
|
|
283
|
+
method: "GET"
|
|
284
|
+
} }
|
|
277
285
|
};
|
|
278
286
|
|
|
279
287
|
//#endregion
|
|
@@ -473,6 +481,35 @@ async function getSessionDataFromCookie(request, cookieName, cookieSecret) {
|
|
|
473
481
|
return null;
|
|
474
482
|
}
|
|
475
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* Fetch session data from upstream using session token cookie
|
|
486
|
+
*
|
|
487
|
+
* @param sessionTokenCookie - Session token cookie string (can be Set-Cookie header or "name=value" format)
|
|
488
|
+
* @param baseUrl - Auth server base URL
|
|
489
|
+
* @returns Session data from upstream
|
|
490
|
+
*/
|
|
491
|
+
async function fetchSessionWithCookie(sessionTokenCookie, baseUrl) {
|
|
492
|
+
let cookieName;
|
|
493
|
+
let cookieValue;
|
|
494
|
+
if (sessionTokenCookie.includes("=")) {
|
|
495
|
+
const [name, ...valueParts] = sessionTokenCookie.split(";")[0].trim().split("=");
|
|
496
|
+
cookieName = name.trim();
|
|
497
|
+
cookieValue = valueParts.join("=").trim();
|
|
498
|
+
} else throw new Error("Invalid session token cookie format");
|
|
499
|
+
if (!cookieName.includes("session_token")) throw new Error("session_token not found in cookie");
|
|
500
|
+
const response = await fetch(`${baseUrl}/get-session`, {
|
|
501
|
+
headers: { Cookie: `${cookieName}=${cookieValue}` },
|
|
502
|
+
signal: AbortSignal.timeout(3e3)
|
|
503
|
+
});
|
|
504
|
+
if (!response.ok) throw new Error(`Failed to fetch session data: ${response.status} ${response.statusText}`);
|
|
505
|
+
let body;
|
|
506
|
+
try {
|
|
507
|
+
body = await response.json();
|
|
508
|
+
} catch (error) {
|
|
509
|
+
throw new Error(`Failed to parse /get-session response as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
510
|
+
}
|
|
511
|
+
return parseSessionData(body);
|
|
512
|
+
}
|
|
476
513
|
|
|
477
514
|
//#endregion
|
|
478
515
|
//#region src/server/errors.ts
|
|
@@ -506,6 +543,253 @@ async function validateSessionData(sessionDataString, cookieSecret) {
|
|
|
506
543
|
}
|
|
507
544
|
}
|
|
508
545
|
|
|
546
|
+
//#endregion
|
|
547
|
+
//#region src/server/session/minting.ts
|
|
548
|
+
/**
|
|
549
|
+
* Core minting logic - creates session_data cookie from session token
|
|
550
|
+
*
|
|
551
|
+
* @param sessionTokenCookie - Session token cookie string (format: "name=value")
|
|
552
|
+
* @param baseUrl - Auth server base URL
|
|
553
|
+
* @param cookieConfig - Cookie configuration
|
|
554
|
+
* @returns Set-Cookie string or null on error
|
|
555
|
+
*/
|
|
556
|
+
async function mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig) {
|
|
557
|
+
try {
|
|
558
|
+
const sessionData = await fetchSessionWithCookie(sessionTokenCookie, baseUrl);
|
|
559
|
+
if (!sessionData.session) return null;
|
|
560
|
+
const { value: signedData, expiresAt } = await signSessionDataCookie(sessionData, cookieConfig.secret, cookieConfig.sessionDataTtl);
|
|
561
|
+
const maxAge = Math.floor((expiresAt.getTime() - Date.now()) / 1e3);
|
|
562
|
+
return serializeSetCookie({
|
|
563
|
+
name: NEON_AUTH_SESSION_DATA_COOKIE_NAME,
|
|
564
|
+
value: signedData,
|
|
565
|
+
path: "/",
|
|
566
|
+
domain: cookieConfig.domain,
|
|
567
|
+
httpOnly: true,
|
|
568
|
+
secure: true,
|
|
569
|
+
sameSite: cookieConfig.sameSite ?? "strict",
|
|
570
|
+
maxAge
|
|
571
|
+
});
|
|
572
|
+
} catch (error) {
|
|
573
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
574
|
+
console.error("[mintSessionDataCookie] Failed to mint session_data cookie:", {
|
|
575
|
+
error: errorMessage,
|
|
576
|
+
...process.env.NODE_ENV !== "production" && { stack: error instanceof Error ? error.stack : void 0 }
|
|
577
|
+
});
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Utility A: Mint session_data cookie when session_token is updated by upstream
|
|
583
|
+
*
|
|
584
|
+
* Checks response headers for session_token in Set-Cookie, then mints session_data.
|
|
585
|
+
* Handles token deletion (max-age=0) by returning deletion cookie.
|
|
586
|
+
*
|
|
587
|
+
* Use case: Response handling in proxy/middleware after upstream auth calls
|
|
588
|
+
*
|
|
589
|
+
* @param responseHeaders - Response headers from upstream auth server
|
|
590
|
+
* @param baseUrl - Auth server base URL
|
|
591
|
+
* @param cookieConfig - Cookie configuration
|
|
592
|
+
* @returns Set-Cookie string for session_data or null if no action needed
|
|
593
|
+
*/
|
|
594
|
+
async function mintSessionDataFromResponse(responseHeaders, baseUrl, cookieConfig) {
|
|
595
|
+
const sessionTokenCookie = responseHeaders.getSetCookie().find((cookie) => cookie.includes("session_token"));
|
|
596
|
+
if (!sessionTokenCookie) return null;
|
|
597
|
+
if (sessionTokenCookie.toLowerCase().includes("max-age=0")) return serializeSetCookie({
|
|
598
|
+
name: NEON_AUTH_SESSION_DATA_COOKIE_NAME,
|
|
599
|
+
value: "",
|
|
600
|
+
path: "/",
|
|
601
|
+
domain: cookieConfig.domain,
|
|
602
|
+
httpOnly: true,
|
|
603
|
+
secure: true,
|
|
604
|
+
sameSite: cookieConfig.sameSite ?? "strict",
|
|
605
|
+
maxAge: 0
|
|
606
|
+
});
|
|
607
|
+
return await mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Utility B: Mint/refresh session_data cookie from existing session_token
|
|
611
|
+
*
|
|
612
|
+
* Extracts session_token from request cookies and mints a fresh session_data cookie.
|
|
613
|
+
* Use case: Cache misses, expired cookies, proactive refresh
|
|
614
|
+
*
|
|
615
|
+
* @param sessionTokenCookie - Session token cookie string (format: "name=value")
|
|
616
|
+
* @param baseUrl - Auth server base URL
|
|
617
|
+
* @param cookieConfig - Cookie configuration
|
|
618
|
+
* @returns Set-Cookie string for session_data or null on error
|
|
619
|
+
*/
|
|
620
|
+
async function mintSessionDataFromToken(sessionTokenCookie, baseUrl, cookieConfig) {
|
|
621
|
+
return await mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/server/client-factory.ts
|
|
626
|
+
function createAuthServerInternal(config) {
|
|
627
|
+
const { baseUrl, context: getContext, cookieSecret, sessionDataTtl, domain, sameSite } = config;
|
|
628
|
+
const effectiveSameSite = sameSite ?? "strict";
|
|
629
|
+
const fetchWithAuth = async (path, method, args) => {
|
|
630
|
+
const ctx = await getContext();
|
|
631
|
+
const cookies$1 = await ctx.getCookies();
|
|
632
|
+
const origin = await ctx.getOrigin();
|
|
633
|
+
const framework = ctx.getFramework();
|
|
634
|
+
const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
635
|
+
const { query, fetchOptions: _fetchOptions, ...body } = args || {};
|
|
636
|
+
if (query && typeof query === "object") {
|
|
637
|
+
const queryParams = query;
|
|
638
|
+
for (const [key, value] of Object.entries(queryParams)) if (value !== void 0 && value !== null) url.searchParams.set(key, String(value));
|
|
639
|
+
}
|
|
640
|
+
const headers$1 = {
|
|
641
|
+
Cookie: cookies$1,
|
|
642
|
+
Origin: origin,
|
|
643
|
+
[NEON_AUTH_SERVER_PROXY_HEADER]: framework
|
|
644
|
+
};
|
|
645
|
+
let requestBody;
|
|
646
|
+
if (method === "POST") {
|
|
647
|
+
headers$1["Content-Type"] = "application/json";
|
|
648
|
+
requestBody = JSON.stringify(Object.keys(body).length > 0 ? body : {});
|
|
649
|
+
}
|
|
650
|
+
const response = await fetch(url.toString(), {
|
|
651
|
+
method,
|
|
652
|
+
headers: headers$1,
|
|
653
|
+
body: requestBody
|
|
654
|
+
});
|
|
655
|
+
const setCookieHeaders = response.headers.getSetCookie();
|
|
656
|
+
if (setCookieHeaders.length > 0) {
|
|
657
|
+
for (const setCookieHeader of setCookieHeaders) {
|
|
658
|
+
const parsedCookies = parseSetCookies(setCookieHeader);
|
|
659
|
+
for (const cookie of parsedCookies) {
|
|
660
|
+
const cookieOptions = {
|
|
661
|
+
...cookie,
|
|
662
|
+
domain,
|
|
663
|
+
partitioned: void 0,
|
|
664
|
+
sameSite: effectiveSameSite
|
|
665
|
+
};
|
|
666
|
+
await ctx.setCookie(cookie.name, cookie.value, cookieOptions);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
try {
|
|
670
|
+
const sessionDataCookie = await mintSessionDataFromResponse(response.headers, baseUrl, {
|
|
671
|
+
secret: cookieSecret,
|
|
672
|
+
sessionDataTtl,
|
|
673
|
+
domain,
|
|
674
|
+
sameSite
|
|
675
|
+
});
|
|
676
|
+
if (sessionDataCookie) {
|
|
677
|
+
const [parsedSessionData] = parseSetCookies(sessionDataCookie);
|
|
678
|
+
if (parsedSessionData) await ctx.setCookie(parsedSessionData.name, parsedSessionData.value, parsedSessionData);
|
|
679
|
+
}
|
|
680
|
+
} catch (error) {
|
|
681
|
+
console.error("[fetchWithAuth] Failed to mint session data cookie:", error);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const responseData = await response.json().catch(() => null);
|
|
685
|
+
if (!response.ok) {
|
|
686
|
+
const normalized = normalizeBetterAuthError({
|
|
687
|
+
status: response.status,
|
|
688
|
+
statusText: response.statusText,
|
|
689
|
+
message: responseData?.message || response.statusText,
|
|
690
|
+
code: responseData?.code,
|
|
691
|
+
body: responseData
|
|
692
|
+
});
|
|
693
|
+
return {
|
|
694
|
+
data: null,
|
|
695
|
+
error: {
|
|
696
|
+
message: normalized.message,
|
|
697
|
+
status: normalized.status ?? response.status,
|
|
698
|
+
statusText: response.statusText,
|
|
699
|
+
code: normalized.code
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
data: responseData,
|
|
705
|
+
error: null
|
|
706
|
+
};
|
|
707
|
+
};
|
|
708
|
+
const baseServer = createApiProxy(API_ENDPOINTS, fetchWithAuth);
|
|
709
|
+
const originalGetSession = baseServer.getSession;
|
|
710
|
+
baseServer.getSession = async (...args) => {
|
|
711
|
+
const [data] = args;
|
|
712
|
+
if (!(data?.query?.disableCookieCache === "true")) try {
|
|
713
|
+
const cookiesString = await (await getContext()).getCookies();
|
|
714
|
+
const hasSessionToken = cookiesString.includes(NEON_AUTH_SESSION_COOKIE_NAME);
|
|
715
|
+
const sessionDataCookie = parseCookieValue(cookiesString, NEON_AUTH_SESSION_DATA_COOKIE_NAME);
|
|
716
|
+
if (sessionDataCookie && hasSessionToken) {
|
|
717
|
+
const result = await validateSessionData(sessionDataCookie, cookieSecret);
|
|
718
|
+
if (result.valid && result.payload) return {
|
|
719
|
+
data: result.payload,
|
|
720
|
+
error: null
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.error("[auth.getSession] Cookie validation error:", error);
|
|
725
|
+
}
|
|
726
|
+
return originalGetSession(...args);
|
|
727
|
+
};
|
|
728
|
+
return baseServer;
|
|
729
|
+
}
|
|
730
|
+
function isEndpointConfig(value) {
|
|
731
|
+
return typeof value === "object" && value !== null && "path" in value && "method" in value;
|
|
732
|
+
}
|
|
733
|
+
function createApiProxy(endpoints, fetchFn) {
|
|
734
|
+
return new Proxy({}, {
|
|
735
|
+
get(target, prop) {
|
|
736
|
+
if (prop in target) return target[prop];
|
|
737
|
+
const endpoint = endpoints[prop];
|
|
738
|
+
if (!endpoint) return;
|
|
739
|
+
if (isEndpointConfig(endpoint)) return (args) => fetchFn(endpoint.path, endpoint.method, args);
|
|
740
|
+
return createApiProxy(endpoint, fetchFn);
|
|
741
|
+
},
|
|
742
|
+
set(target, prop, value) {
|
|
743
|
+
target[prop] = value;
|
|
744
|
+
return true;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
//#endregion
|
|
750
|
+
//#region src/next/server/adapter.ts
|
|
751
|
+
/**
|
|
752
|
+
* Creates a Next.js-specific RequestContext that reads cookies and headers
|
|
753
|
+
* from next/headers and handles cookie setting.
|
|
754
|
+
*/
|
|
755
|
+
async function createNextRequestContext() {
|
|
756
|
+
const cookieStore = await cookies();
|
|
757
|
+
const headerStore = await headers();
|
|
758
|
+
return {
|
|
759
|
+
getCookies() {
|
|
760
|
+
return extractNeonAuthCookies(headerStore);
|
|
761
|
+
},
|
|
762
|
+
setCookie(name, value, options) {
|
|
763
|
+
cookieStore.set(name, value, options);
|
|
764
|
+
},
|
|
765
|
+
getHeader(name) {
|
|
766
|
+
return headerStore.get(name) ?? null;
|
|
767
|
+
},
|
|
768
|
+
getOrigin() {
|
|
769
|
+
return headerStore.get("origin") || headerStore.get("referer")?.split("/").slice(0, 3).join("/") || "";
|
|
770
|
+
},
|
|
771
|
+
getFramework() {
|
|
772
|
+
return "nextjs";
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region src/server/config.ts
|
|
779
|
+
/**
|
|
780
|
+
* Framework-agnostic configuration types for Neon Auth
|
|
781
|
+
*/
|
|
782
|
+
/**
|
|
783
|
+
* Validates cookie configuration meets security requirements
|
|
784
|
+
* @param cookies - The cookie configuration to validate
|
|
785
|
+
* @throws Error if secret is too short (< 32 characters)
|
|
786
|
+
*/
|
|
787
|
+
function validateCookieConfig(cookies$1) {
|
|
788
|
+
if (!cookies$1.secret) throw new Error(ERRORS.MISSING_COOKIE_SECRET);
|
|
789
|
+
if (cookies$1.secret.length < 32) throw new Error(ERRORS.COOKIE_SECRET_TOO_SHORT);
|
|
790
|
+
if (cookies$1.sessionDataTtl !== void 0 && cookies$1.sessionDataTtl <= 0) throw new Error(ERRORS.INVALID_SESSION_DATA_TTL);
|
|
791
|
+
}
|
|
792
|
+
|
|
509
793
|
//#endregion
|
|
510
794
|
//#region src/server/proxy/request.ts
|
|
511
795
|
const PROXY_HEADERS = [
|
|
@@ -612,8 +896,8 @@ const RESPONSE_HEADERS_ALLOWLIST = [
|
|
|
612
896
|
* @returns New Response with proxied headers and session data cookie
|
|
613
897
|
*/
|
|
614
898
|
const handleAuthResponse = async (response, baseUrl, cookieConfig) => {
|
|
615
|
-
const responseHeaders = prepareResponseHeaders(response, cookieConfig
|
|
616
|
-
const sessionDataCookie = await
|
|
899
|
+
const responseHeaders = prepareResponseHeaders(response, cookieConfig);
|
|
900
|
+
const sessionDataCookie = await mintSessionDataFromResponse(response.headers, baseUrl, cookieConfig);
|
|
617
901
|
if (sessionDataCookie) responseHeaders.append("Set-Cookie", sessionDataCookie);
|
|
618
902
|
return new Response(response.body, {
|
|
619
903
|
status: response.status,
|
|
@@ -621,85 +905,27 @@ const handleAuthResponse = async (response, baseUrl, cookieConfig) => {
|
|
|
621
905
|
headers: responseHeaders
|
|
622
906
|
});
|
|
623
907
|
};
|
|
624
|
-
const prepareResponseHeaders = (response,
|
|
908
|
+
const prepareResponseHeaders = (response, cookieConfig) => {
|
|
625
909
|
const headers$1 = new Headers();
|
|
910
|
+
const effectiveSameSite = cookieConfig.sameSite ?? "strict";
|
|
911
|
+
const { domain } = cookieConfig;
|
|
626
912
|
for (const header of RESPONSE_HEADERS_ALLOWLIST) if (header === "set-cookie") {
|
|
627
913
|
const cookies$1 = response.headers.getSetCookie();
|
|
628
|
-
for (const cookieHeader of cookies$1)
|
|
914
|
+
for (const cookieHeader of cookies$1) {
|
|
629
915
|
const parsedCookies = parseSetCookies(cookieHeader);
|
|
630
916
|
for (const parsedCookie of parsedCookies) {
|
|
631
|
-
parsedCookie.
|
|
917
|
+
parsedCookie.partitioned = void 0;
|
|
918
|
+
parsedCookie.sameSite = effectiveSameSite;
|
|
919
|
+
if (domain) parsedCookie.domain = domain;
|
|
632
920
|
headers$1.append("Set-Cookie", serializeSetCookie(parsedCookie));
|
|
633
921
|
}
|
|
634
|
-
}
|
|
922
|
+
}
|
|
635
923
|
} else {
|
|
636
924
|
const value = response.headers.get(header);
|
|
637
925
|
if (value) headers$1.set(header, value);
|
|
638
926
|
}
|
|
639
927
|
return headers$1;
|
|
640
928
|
};
|
|
641
|
-
async function mintSessionData(headers$1, baseUrl, cookieConfig) {
|
|
642
|
-
const { secret, sessionDataTtl, domain } = cookieConfig;
|
|
643
|
-
const sessionToken = headers$1.getSetCookie().find((cookie) => cookie.includes("session_token"));
|
|
644
|
-
if (!sessionToken) return null;
|
|
645
|
-
if (sessionToken.toLowerCase().includes("max-age=0")) return serializeSetCookie({
|
|
646
|
-
name: NEON_AUTH_SESSION_DATA_COOKIE_NAME,
|
|
647
|
-
value: "",
|
|
648
|
-
path: "/",
|
|
649
|
-
domain,
|
|
650
|
-
httpOnly: true,
|
|
651
|
-
secure: true,
|
|
652
|
-
sameSite: "lax",
|
|
653
|
-
maxAge: 0
|
|
654
|
-
});
|
|
655
|
-
try {
|
|
656
|
-
const sessionData = await fetchSessionWithCookie(sessionToken, baseUrl);
|
|
657
|
-
if (sessionData.session) {
|
|
658
|
-
const { value: signedData, expiresAt } = await signSessionDataCookie(sessionData, secret, sessionDataTtl);
|
|
659
|
-
return serializeSetCookie({
|
|
660
|
-
name: NEON_AUTH_SESSION_DATA_COOKIE_NAME,
|
|
661
|
-
value: signedData,
|
|
662
|
-
path: "/",
|
|
663
|
-
domain,
|
|
664
|
-
httpOnly: true,
|
|
665
|
-
secure: true,
|
|
666
|
-
sameSite: "lax",
|
|
667
|
-
maxAge: Math.floor((expiresAt.getTime() - Date.now()) / 1e3)
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
} catch (error) {
|
|
671
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
672
|
-
const errorContext = {
|
|
673
|
-
error: errorMessage,
|
|
674
|
-
setCookieHeaderLength: sessionToken?.length || 0
|
|
675
|
-
};
|
|
676
|
-
if (errorMessage.includes("session_token not found")) console.warn("[mintSessionData] Session token missing in set-cookie:", errorContext);
|
|
677
|
-
else if (errorMessage.includes("Failed to fetch session data")) console.error("[mintSessionData] Upstream /get-session request failed:", errorContext);
|
|
678
|
-
else if (errorMessage.includes("NEON_AUTH_COOKIE_SECRET")) console.error("[mintSessionData] Cookie secret configuration error:", errorContext);
|
|
679
|
-
else if (errorMessage.includes("Invalid date")) console.error("[mintSessionData] Date parsing error:", errorContext);
|
|
680
|
-
else console.error("[mintSessionData] Unexpected error:", {
|
|
681
|
-
...errorContext,
|
|
682
|
-
...process.env.NODE_ENV !== "production" && { stack: error instanceof Error ? error.stack : void 0 }
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
return null;
|
|
686
|
-
}
|
|
687
|
-
async function fetchSessionWithCookie(setCookieHeader, baseUrl) {
|
|
688
|
-
const sessionToken = parseSetCookies(setCookieHeader).find((c) => c.name.includes("session_token"));
|
|
689
|
-
if (!sessionToken) throw new Error("session_token not found in set-cookie header");
|
|
690
|
-
const response = await fetch(`${baseUrl}/get-session`, {
|
|
691
|
-
headers: { Cookie: `${sessionToken.name}=${sessionToken.value}` },
|
|
692
|
-
signal: AbortSignal.timeout(3e3)
|
|
693
|
-
});
|
|
694
|
-
if (!response.ok) throw new Error(`Failed to fetch session data: ${response.status} ${response.statusText}`);
|
|
695
|
-
let body;
|
|
696
|
-
try {
|
|
697
|
-
body = await response.json();
|
|
698
|
-
} catch (error) {
|
|
699
|
-
throw new Error(`Failed to parse /get-session response as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
700
|
-
}
|
|
701
|
-
return parseSessionData(body);
|
|
702
|
-
}
|
|
703
929
|
|
|
704
930
|
//#endregion
|
|
705
931
|
//#region src/server/session/cache-handler.ts
|
|
@@ -707,38 +933,68 @@ async function fetchSessionWithCookie(setCookieHeader, baseUrl) {
|
|
|
707
933
|
* Attempts to retrieve session data from cookie cache
|
|
708
934
|
* Returns Response with session data if cache hit, null otherwise
|
|
709
935
|
*
|
|
936
|
+
* If session_data cookie is missing or invalid, attempts to mint a new one
|
|
937
|
+
* from the session_token cookie (reactive minting).
|
|
938
|
+
*
|
|
710
939
|
* This is the framework-agnostic session cache optimization used by API handlers.
|
|
711
940
|
*
|
|
712
941
|
* @param request - Standard Web API Request object
|
|
713
|
-
* @param
|
|
942
|
+
* @param baseUrl - Auth server base URL for upstream calls
|
|
943
|
+
* @param cookieConfig - Cookie configuration (secret, TTL, domain)
|
|
714
944
|
* @returns Response with session data JSON if cache hit, null if miss/disabled
|
|
715
945
|
*/
|
|
716
|
-
async function trySessionCache(request,
|
|
946
|
+
async function trySessionCache(request, baseUrl, cookieConfig) {
|
|
717
947
|
if (new URL(request.url).searchParams.get("disableCookieCache") === "true") return null;
|
|
718
|
-
|
|
948
|
+
const cookieHeader = request.headers.get("cookie") || "";
|
|
949
|
+
if (!cookieHeader.includes(NEON_AUTH_SESSION_COOKIE_NAME)) return null;
|
|
950
|
+
const hasSessionData = parseCookies(cookieHeader).has(NEON_AUTH_SESSION_DATA_COOKIE_NAME);
|
|
951
|
+
const mintAndReturn = async () => {
|
|
952
|
+
const sessionTokenCookie = extractSessionTokenCookie(cookieHeader);
|
|
953
|
+
if (!sessionTokenCookie) return null;
|
|
954
|
+
const sessionDataCookieString = await mintSessionDataFromToken(sessionTokenCookie, baseUrl, cookieConfig);
|
|
955
|
+
if (!sessionDataCookieString) return null;
|
|
956
|
+
try {
|
|
957
|
+
const sessionData = await fetchSessionWithCookie(sessionTokenCookie, baseUrl);
|
|
958
|
+
if (!sessionData.session) return null;
|
|
959
|
+
const response = Response.json(sessionData);
|
|
960
|
+
response.headers.set("Set-Cookie", sessionDataCookieString);
|
|
961
|
+
return response;
|
|
962
|
+
} catch (error) {
|
|
963
|
+
console.error("[trySessionCache] Failed to fetch session after minting cookie:", error);
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
if (!hasSessionData) return await mintAndReturn();
|
|
719
968
|
try {
|
|
720
|
-
const sessionData = await getSessionDataFromCookie(request, NEON_AUTH_SESSION_DATA_COOKIE_NAME,
|
|
969
|
+
const sessionData = await getSessionDataFromCookie(request, NEON_AUTH_SESSION_DATA_COOKIE_NAME, cookieConfig.secret);
|
|
721
970
|
if (sessionData && sessionData.session) return Response.json(sessionData);
|
|
971
|
+
return await mintAndReturn();
|
|
722
972
|
} catch (error) {
|
|
723
973
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
724
974
|
const errorName = error instanceof Error ? error.name : "Unknown";
|
|
725
|
-
if (errorName === "JWTExpired") console.debug("[trySessionCache] Session cookie expired
|
|
975
|
+
if (errorName === "JWTExpired") console.debug("[trySessionCache] Session cookie expired, minting new one:", {
|
|
726
976
|
error: errorMessage,
|
|
727
|
-
errorType: errorName,
|
|
728
977
|
url: request.url
|
|
729
978
|
});
|
|
730
|
-
else if (errorName === "JWTInvalid" || errorName === "JWTClaimValidationFailed") console.warn("[trySessionCache] Invalid session cookie
|
|
979
|
+
else if (errorName === "JWTInvalid" || errorName === "JWTClaimValidationFailed") console.warn("[trySessionCache] Invalid session cookie, minting new one:", {
|
|
731
980
|
error: errorMessage,
|
|
732
|
-
errorType: errorName,
|
|
733
981
|
url: request.url
|
|
734
982
|
});
|
|
735
|
-
else console.error("[trySessionCache] Unexpected
|
|
983
|
+
else console.error("[trySessionCache] Unexpected validation error:", {
|
|
736
984
|
error: errorMessage,
|
|
737
|
-
errorType: errorName,
|
|
738
985
|
url: request.url
|
|
739
986
|
});
|
|
987
|
+
return await mintAndReturn();
|
|
740
988
|
}
|
|
741
|
-
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Extract session_token cookie value from cookie header
|
|
992
|
+
* @internal
|
|
993
|
+
*/
|
|
994
|
+
function extractSessionTokenCookie(cookieHeader) {
|
|
995
|
+
const sessionToken = parseCookies(cookieHeader).get(NEON_AUTH_SESSION_COOKIE_NAME);
|
|
996
|
+
if (!sessionToken) return null;
|
|
997
|
+
return `${NEON_AUTH_SESSION_COOKIE_NAME}=${sessionToken}`;
|
|
742
998
|
}
|
|
743
999
|
|
|
744
1000
|
//#endregion
|
|
@@ -758,173 +1014,24 @@ async function trySessionCache(request, cookieSecret) {
|
|
|
758
1014
|
* @returns Standard Web API Response
|
|
759
1015
|
*/
|
|
760
1016
|
async function handleAuthProxyRequest(config) {
|
|
761
|
-
const { request, path, baseUrl, cookieSecret, sessionDataTtl, domain } = config;
|
|
1017
|
+
const { request, path, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite } = config;
|
|
762
1018
|
if (path === API_ENDPOINTS.getSession.path && request.method === API_ENDPOINTS.getSession.method) {
|
|
763
|
-
const cachedResponse = await trySessionCache(request,
|
|
1019
|
+
const cachedResponse = await trySessionCache(request, baseUrl, {
|
|
1020
|
+
secret: cookieSecret,
|
|
1021
|
+
sessionDataTtl,
|
|
1022
|
+
domain,
|
|
1023
|
+
sameSite
|
|
1024
|
+
});
|
|
764
1025
|
if (cachedResponse) return cachedResponse;
|
|
765
1026
|
}
|
|
766
1027
|
return await handleAuthResponse(await handleAuthRequest(baseUrl, request, path), baseUrl, {
|
|
767
1028
|
secret: cookieSecret,
|
|
768
1029
|
sessionDataTtl,
|
|
769
|
-
domain
|
|
770
|
-
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
//#endregion
|
|
774
|
-
//#region src/server/client-factory.ts
|
|
775
|
-
function createAuthServerInternal(config) {
|
|
776
|
-
const { baseUrl, context: getContext, cookieSecret, sessionDataTtl, domain } = config;
|
|
777
|
-
const fetchWithAuth = async (path, method, args) => {
|
|
778
|
-
const ctx = await getContext();
|
|
779
|
-
const cookies$1 = await ctx.getCookies();
|
|
780
|
-
const origin = await ctx.getOrigin();
|
|
781
|
-
const framework = ctx.getFramework();
|
|
782
|
-
const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
783
|
-
const { query, fetchOptions: _fetchOptions, ...body } = args || {};
|
|
784
|
-
if (query && typeof query === "object") {
|
|
785
|
-
const queryParams = query;
|
|
786
|
-
for (const [key, value] of Object.entries(queryParams)) if (value !== void 0 && value !== null) url.searchParams.set(key, String(value));
|
|
787
|
-
}
|
|
788
|
-
const headers$1 = {
|
|
789
|
-
Cookie: cookies$1,
|
|
790
|
-
Origin: origin,
|
|
791
|
-
[NEON_AUTH_SERVER_PROXY_HEADER]: framework
|
|
792
|
-
};
|
|
793
|
-
let requestBody;
|
|
794
|
-
if (method === "POST") {
|
|
795
|
-
headers$1["Content-Type"] = "application/json";
|
|
796
|
-
requestBody = JSON.stringify(Object.keys(body).length > 0 ? body : {});
|
|
797
|
-
}
|
|
798
|
-
const response = await fetch(url.toString(), {
|
|
799
|
-
method,
|
|
800
|
-
headers: headers$1,
|
|
801
|
-
body: requestBody
|
|
802
|
-
});
|
|
803
|
-
const setCookieHeaders = response.headers.getSetCookie();
|
|
804
|
-
if (setCookieHeaders.length > 0) {
|
|
805
|
-
for (const setCookieHeader of setCookieHeaders) {
|
|
806
|
-
const parsedCookies = parseSetCookies(setCookieHeader);
|
|
807
|
-
for (const cookie of parsedCookies) {
|
|
808
|
-
const cookieOptions = domain ? {
|
|
809
|
-
...cookie,
|
|
810
|
-
domain
|
|
811
|
-
} : cookie;
|
|
812
|
-
await ctx.setCookie(cookie.name, cookie.value, cookieOptions);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
try {
|
|
816
|
-
const sessionDataCookie = await mintSessionData(response.headers, baseUrl, {
|
|
817
|
-
secret: cookieSecret,
|
|
818
|
-
sessionDataTtl,
|
|
819
|
-
domain
|
|
820
|
-
});
|
|
821
|
-
if (sessionDataCookie) {
|
|
822
|
-
const [parsedSessionData] = parseSetCookies(sessionDataCookie);
|
|
823
|
-
if (parsedSessionData) await ctx.setCookie(parsedSessionData.name, parsedSessionData.value, parsedSessionData);
|
|
824
|
-
}
|
|
825
|
-
} catch (error) {
|
|
826
|
-
console.error("[fetchWithAuth] Failed to mint session data cookie:", error);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
const responseData = await response.json().catch(() => null);
|
|
830
|
-
if (!response.ok) return {
|
|
831
|
-
data: null,
|
|
832
|
-
error: {
|
|
833
|
-
message: responseData?.message || response.statusText,
|
|
834
|
-
status: response.status,
|
|
835
|
-
statusText: response.statusText
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
return {
|
|
839
|
-
data: responseData,
|
|
840
|
-
error: null
|
|
841
|
-
};
|
|
842
|
-
};
|
|
843
|
-
const baseServer = createApiProxy(API_ENDPOINTS, fetchWithAuth);
|
|
844
|
-
const originalGetSession = baseServer.getSession;
|
|
845
|
-
baseServer.getSession = async (...args) => {
|
|
846
|
-
const [data] = args;
|
|
847
|
-
if (!(data?.query?.disableCookieCache === "true")) try {
|
|
848
|
-
const cookiesString = await (await getContext()).getCookies();
|
|
849
|
-
const hasSessionToken = cookiesString.includes(NEON_AUTH_SESSION_COOKIE_NAME);
|
|
850
|
-
const sessionDataCookie = parseCookieValue(cookiesString, NEON_AUTH_SESSION_DATA_COOKIE_NAME);
|
|
851
|
-
if (sessionDataCookie && hasSessionToken) {
|
|
852
|
-
const result = await validateSessionData(sessionDataCookie, cookieSecret);
|
|
853
|
-
if (result.valid && result.payload) return {
|
|
854
|
-
data: result.payload,
|
|
855
|
-
error: null
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
} catch (error) {
|
|
859
|
-
console.error("[auth.getSession] Cookie validation error:", error);
|
|
860
|
-
}
|
|
861
|
-
return originalGetSession(...args);
|
|
862
|
-
};
|
|
863
|
-
return baseServer;
|
|
864
|
-
}
|
|
865
|
-
function isEndpointConfig(value) {
|
|
866
|
-
return typeof value === "object" && value !== null && "path" in value && "method" in value;
|
|
867
|
-
}
|
|
868
|
-
function createApiProxy(endpoints, fetchFn) {
|
|
869
|
-
return new Proxy({}, {
|
|
870
|
-
get(target, prop) {
|
|
871
|
-
if (prop in target) return target[prop];
|
|
872
|
-
const endpoint = endpoints[prop];
|
|
873
|
-
if (!endpoint) return;
|
|
874
|
-
if (isEndpointConfig(endpoint)) return (args) => fetchFn(endpoint.path, endpoint.method, args);
|
|
875
|
-
return createApiProxy(endpoint, fetchFn);
|
|
876
|
-
},
|
|
877
|
-
set(target, prop, value) {
|
|
878
|
-
target[prop] = value;
|
|
879
|
-
return true;
|
|
880
|
-
}
|
|
1030
|
+
domain,
|
|
1031
|
+
sameSite
|
|
881
1032
|
});
|
|
882
1033
|
}
|
|
883
1034
|
|
|
884
|
-
//#endregion
|
|
885
|
-
//#region src/next/server/adapter.ts
|
|
886
|
-
/**
|
|
887
|
-
* Creates a Next.js-specific RequestContext that reads cookies and headers
|
|
888
|
-
* from next/headers and handles cookie setting.
|
|
889
|
-
*/
|
|
890
|
-
async function createNextRequestContext() {
|
|
891
|
-
const cookieStore = await cookies();
|
|
892
|
-
const headerStore = await headers();
|
|
893
|
-
return {
|
|
894
|
-
getCookies() {
|
|
895
|
-
return extractNeonAuthCookies(headerStore);
|
|
896
|
-
},
|
|
897
|
-
setCookie(name, value, options) {
|
|
898
|
-
cookieStore.set(name, value, options);
|
|
899
|
-
},
|
|
900
|
-
getHeader(name) {
|
|
901
|
-
return headerStore.get(name) ?? null;
|
|
902
|
-
},
|
|
903
|
-
getOrigin() {
|
|
904
|
-
return headerStore.get("origin") || headerStore.get("referer")?.split("/").slice(0, 3).join("/") || "";
|
|
905
|
-
},
|
|
906
|
-
getFramework() {
|
|
907
|
-
return "nextjs";
|
|
908
|
-
}
|
|
909
|
-
};
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
//#endregion
|
|
913
|
-
//#region src/server/config.ts
|
|
914
|
-
/**
|
|
915
|
-
* Framework-agnostic configuration types for Neon Auth
|
|
916
|
-
*/
|
|
917
|
-
/**
|
|
918
|
-
* Validates cookie configuration meets security requirements
|
|
919
|
-
* @param cookies - The cookie configuration to validate
|
|
920
|
-
* @throws Error if secret is too short (< 32 characters)
|
|
921
|
-
*/
|
|
922
|
-
function validateCookieConfig(cookies$1) {
|
|
923
|
-
if (!cookies$1.secret) throw new Error(ERRORS.MISSING_COOKIE_SECRET);
|
|
924
|
-
if (cookies$1.secret.length < 32) throw new Error(ERRORS.COOKIE_SECRET_TOO_SHORT);
|
|
925
|
-
if (cookies$1.sessionDataTtl !== void 0 && cookies$1.sessionDataTtl <= 0) throw new Error(ERRORS.INVALID_SESSION_DATA_TTL);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
1035
|
//#endregion
|
|
929
1036
|
//#region src/next/server/handler.ts
|
|
930
1037
|
/**
|
|
@@ -964,7 +1071,8 @@ function authApiHandler(config) {
|
|
|
964
1071
|
baseUrl,
|
|
965
1072
|
cookieSecret: cookies$1.secret,
|
|
966
1073
|
sessionDataTtl: cookies$1.sessionDataTtl,
|
|
967
|
-
domain: cookies$1.domain
|
|
1074
|
+
domain: cookies$1.domain,
|
|
1075
|
+
sameSite: cookies$1.sameSite
|
|
968
1076
|
});
|
|
969
1077
|
};
|
|
970
1078
|
return {
|
|
@@ -1003,7 +1111,7 @@ function needsSessionVerification(request) {
|
|
|
1003
1111
|
* @param domain - Optional cookie domain
|
|
1004
1112
|
* @returns Exchange result with redirect URL and cookies, or null if exchange not needed/failed
|
|
1005
1113
|
*/
|
|
1006
|
-
async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain) {
|
|
1114
|
+
async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite) {
|
|
1007
1115
|
const url = new URL(request.url);
|
|
1008
1116
|
const verifier = url.searchParams.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
|
|
1009
1117
|
const cookieHeader = request.headers.get("cookie");
|
|
@@ -1016,7 +1124,8 @@ async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl
|
|
|
1016
1124
|
}), "get-session"), baseUrl, {
|
|
1017
1125
|
secret: cookieSecret,
|
|
1018
1126
|
sessionDataTtl,
|
|
1019
|
-
domain
|
|
1127
|
+
domain,
|
|
1128
|
+
sameSite
|
|
1020
1129
|
});
|
|
1021
1130
|
if (response.ok) {
|
|
1022
1131
|
const setCookieHeaders = response.headers.getSetCookie();
|
|
@@ -1091,10 +1200,11 @@ function checkSessionRequired(pathname, skipRoutes, loginUrl, session) {
|
|
|
1091
1200
|
* @returns Decision object indicating what action to take
|
|
1092
1201
|
*/
|
|
1093
1202
|
async function processAuthMiddleware(config) {
|
|
1094
|
-
const { request, pathname, skipRoutes, loginUrl, baseUrl, cookieSecret, sessionDataTtl, domain } = config;
|
|
1203
|
+
const { request, pathname, skipRoutes, loginUrl, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite } = config;
|
|
1204
|
+
const effectiveSameSite = sameSite ?? "strict";
|
|
1095
1205
|
if (pathname.startsWith(loginUrl)) return { action: "allow" };
|
|
1096
1206
|
if (needsSessionVerification(request)) {
|
|
1097
|
-
const exchangeResult = await exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain);
|
|
1207
|
+
const exchangeResult = await exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite);
|
|
1098
1208
|
if (exchangeResult !== null) return {
|
|
1099
1209
|
action: "redirect_oauth",
|
|
1100
1210
|
redirectUrl: exchangeResult.redirectUrl,
|
|
@@ -1108,6 +1218,7 @@ async function processAuthMiddleware(config) {
|
|
|
1108
1218
|
session: null,
|
|
1109
1219
|
user: null
|
|
1110
1220
|
};
|
|
1221
|
+
let sessionCookies = [];
|
|
1111
1222
|
if (hasSessionToken) {
|
|
1112
1223
|
const sessionResponse = await handleAuthProxyRequest({
|
|
1113
1224
|
request,
|
|
@@ -1115,16 +1226,19 @@ async function processAuthMiddleware(config) {
|
|
|
1115
1226
|
baseUrl,
|
|
1116
1227
|
cookieSecret,
|
|
1117
1228
|
sessionDataTtl,
|
|
1118
|
-
domain
|
|
1229
|
+
domain,
|
|
1230
|
+
sameSite
|
|
1119
1231
|
});
|
|
1120
1232
|
if (sessionResponse.ok) {
|
|
1121
1233
|
const data = await sessionResponse.json().catch(() => null);
|
|
1122
1234
|
if (data) sessionData = data;
|
|
1123
1235
|
}
|
|
1236
|
+
sessionCookies = sessionResponse.headers.getSetCookie();
|
|
1124
1237
|
}
|
|
1125
1238
|
if (checkSessionRequired(pathname, skipRoutes, loginUrl, sessionData).allowed) return {
|
|
1126
1239
|
action: "allow",
|
|
1127
|
-
headers: { [NEON_AUTH_HEADER_MIDDLEWARE_NAME]: "true" }
|
|
1240
|
+
headers: { [NEON_AUTH_HEADER_MIDDLEWARE_NAME]: "true" },
|
|
1241
|
+
cookies: sessionCookies
|
|
1128
1242
|
};
|
|
1129
1243
|
const cookies$1 = [];
|
|
1130
1244
|
if (hasStaleSessionData) cookies$1.push(serializeSetCookie({
|
|
@@ -1134,7 +1248,7 @@ async function processAuthMiddleware(config) {
|
|
|
1134
1248
|
domain,
|
|
1135
1249
|
httpOnly: true,
|
|
1136
1250
|
secure: true,
|
|
1137
|
-
sameSite:
|
|
1251
|
+
sameSite: effectiveSameSite,
|
|
1138
1252
|
maxAge: 0
|
|
1139
1253
|
}));
|
|
1140
1254
|
return {
|
|
@@ -1193,13 +1307,16 @@ function neonAuthMiddleware(config) {
|
|
|
1193
1307
|
baseUrl,
|
|
1194
1308
|
cookieSecret: cookies$1.secret,
|
|
1195
1309
|
sessionDataTtl: cookies$1.sessionDataTtl,
|
|
1196
|
-
domain: cookies$1.domain
|
|
1310
|
+
domain: cookies$1.domain,
|
|
1311
|
+
sameSite: cookies$1.sameSite
|
|
1197
1312
|
});
|
|
1198
1313
|
switch (result.action) {
|
|
1199
1314
|
case "allow": {
|
|
1200
1315
|
const headers$1 = new Headers(request.headers);
|
|
1201
1316
|
if (result.headers) for (const [key, value] of Object.entries(result.headers)) headers$1.set(key, value);
|
|
1202
|
-
|
|
1317
|
+
const response = NextResponse.next({ request: { headers: headers$1 } });
|
|
1318
|
+
if (result.cookies) for (const cookie of result.cookies) response.headers.append("Set-Cookie", cookie);
|
|
1319
|
+
return response;
|
|
1203
1320
|
}
|
|
1204
1321
|
case "redirect_oauth": {
|
|
1205
1322
|
const oauthHeaders = new Headers();
|
|
@@ -1319,7 +1436,8 @@ function createNeonAuth(config) {
|
|
|
1319
1436
|
context: createNextRequestContext,
|
|
1320
1437
|
cookieSecret: cookies$1.secret,
|
|
1321
1438
|
sessionDataTtl: cookies$1.sessionDataTtl,
|
|
1322
|
-
domain: cookies$1.domain
|
|
1439
|
+
domain: cookies$1.domain,
|
|
1440
|
+
sameSite: cookies$1.sameSite
|
|
1323
1441
|
});
|
|
1324
1442
|
/**
|
|
1325
1443
|
* Creates API route handlers for Next.js
|
|
@@ -1370,4 +1488,4 @@ function createNeonAuth(config) {
|
|
|
1370
1488
|
}
|
|
1371
1489
|
|
|
1372
1490
|
//#endregion
|
|
1373
|
-
export { createNeonAuth };
|
|
1491
|
+
export { AuthApiError, AuthError, createNeonAuth, isAuthApiError, isAuthError };
|