@neondatabase/auth 0.3.0-beta → 0.4.1-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.
Files changed (33) hide show
  1. package/README.md +22 -7
  2. package/codemods/migrate-auth-ui-imports.mjs +439 -0
  3. package/dist/{adapter-core-D00qcqMo.mjs → adapter-core-BFMM3lwe.mjs} +21 -11
  4. package/dist/{adapter-core-B9uDhoYq.d.mts → adapter-core-ClY-p_AI.d.mts} +325 -190
  5. package/dist/auth-interface-Clz-oWq1.d.mts +8 -0
  6. package/dist/better-auth-helpers-Bkezghej.mjs +541 -0
  7. package/dist/{better-auth-react-adapter-Xdj-69i9.mjs → better-auth-react-adapter-DZTZVVnk.mjs} +1 -1
  8. package/dist/{better-auth-react-adapter-BO4jLN4H.d.mts → better-auth-react-adapter-iJMZCLUI.d.mts} +388 -301
  9. package/dist/index.d.mts +5 -4
  10. package/dist/index.mjs +4 -3
  11. package/dist/{neon-auth-DBOB8sXF.mjs → neon-auth-VDrC3GwX.mjs} +1 -1
  12. package/dist/next/index.d.mts +144 -56
  13. package/dist/next/index.mjs +5 -4
  14. package/dist/next/server/index.d.mts +131 -14
  15. package/dist/next/server/index.mjs +402 -52
  16. package/dist/react/adapters/index.d.mts +3 -3
  17. package/dist/react/adapters/index.mjs +2 -2
  18. package/dist/react/index.d.mts +4 -4
  19. package/dist/react/index.mjs +2 -2
  20. package/dist/react/ui/index.d.mts +1 -1
  21. package/dist/{supabase-adapter-CIBMebXB.mjs → supabase-adapter-CAyBFrNn.mjs} +3 -514
  22. package/dist/{supabase-adapter-CSDRL1ZU.d.mts → supabase-adapter-cuLnmLDs.d.mts} +390 -303
  23. package/dist/types/index.d.mts +2 -2
  24. package/dist/vanilla/adapters/index.d.mts +4 -3
  25. package/dist/vanilla/adapters/index.mjs +2 -2
  26. package/dist/vanilla/index.d.mts +4 -3
  27. package/dist/vanilla/index.mjs +2 -2
  28. package/llms.txt +2 -2
  29. package/package.json +6 -2
  30. package/dist/constants-Cupc_bln.mjs +0 -28
  31. /package/dist/{index-CPnFzULh.d.mts → index-B0Pd4HOH.d.mts} +0 -0
  32. /package/dist/{index-UW23fDSn.d.mts → index-CzpoWrv9.d.mts} +0 -0
  33. /package/dist/{index-B_Q0Tp1D.d.mts → index-DHryUj7e.d.mts} +0 -0
@@ -1,4 +1,4 @@
1
- import { o as NEON_AUTH_SESSION_VERIFIER_PARAM_NAME } from "../../constants-Cupc_bln.mjs";
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
@@ -558,7 +566,7 @@ async function mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig)
558
566
  domain: cookieConfig.domain,
559
567
  httpOnly: true,
560
568
  secure: true,
561
- sameSite: "lax",
569
+ sameSite: cookieConfig.sameSite ?? "strict",
562
570
  maxAge
563
571
  });
564
572
  } catch (error) {
@@ -593,7 +601,7 @@ async function mintSessionDataFromResponse(responseHeaders, baseUrl, cookieConfi
593
601
  domain: cookieConfig.domain,
594
602
  httpOnly: true,
595
603
  secure: true,
596
- sameSite: "lax",
604
+ sameSite: cookieConfig.sameSite ?? "strict",
597
605
  maxAge: 0
598
606
  });
599
607
  return await mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig);
@@ -613,10 +621,138 @@ async function mintSessionDataFromToken(sessionTokenCookie, baseUrl, cookieConfi
613
621
  return await mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig);
614
622
  }
615
623
 
624
+ //#endregion
625
+ //#region src/server/network-error.ts
626
+ /**
627
+ * Classifies failed upstream `fetch` calls for clearer client responses and logs.
628
+ */
629
+ /**
630
+ * Semver-stable transport error codes surfaced as JSON `code` on synthetic HTTP responses
631
+ * (for example 502 from the proxy) and in logs. Treat adding values as non-breaking;
632
+ * renaming or removing values is a breaking change.
633
+ */
634
+ const NEON_AUTH_NETWORK_ERROR_CODES = [
635
+ "NETWORK_ERROR",
636
+ "NETWORK_DNS",
637
+ "NETWORK_REFUSED",
638
+ "NETWORK_TIMEOUT",
639
+ "NETWORK_TLS",
640
+ "NETWORK_RESET",
641
+ "NETWORK_ABORT"
642
+ ];
643
+ function readErrnoCode(node) {
644
+ if (!node || typeof node !== "object") return void 0;
645
+ const code = node.code;
646
+ return typeof code === "string" ? code : void 0;
647
+ }
648
+ function isAbortError(node) {
649
+ if (node instanceof Error && node.name === "AbortError") return true;
650
+ if (typeof node === "object" && node !== null && "name" in node && node.name === "AbortError") return true;
651
+ return false;
652
+ }
653
+ function isTlsRelated(code, message) {
654
+ if (!code && !message) return false;
655
+ const c = code ?? "";
656
+ if (c.startsWith("ERR_TLS") || c.startsWith("CERT_") || c === "UNABLE_TO_VERIFY_LEAF_SIGNATURE") return true;
657
+ const m = message.toLowerCase();
658
+ return m.includes("certificate") || m.includes("ssl") || m.includes("tls") || m.includes("wrong version number");
659
+ }
660
+ function isLegacyFetchTypeError(node) {
661
+ return node instanceof TypeError && typeof node.message === "string" && node.message.includes("fetch");
662
+ }
663
+ function collectRelatedErrors(error) {
664
+ const seen = /* @__PURE__ */ new Set();
665
+ const list = [];
666
+ function add(e) {
667
+ if (e === void 0 || e === null) return;
668
+ if (typeof e === "object") {
669
+ if (seen.has(e)) return;
670
+ seen.add(e);
671
+ }
672
+ list.push(e);
673
+ if (e instanceof Error && e.cause !== void 0) add(e.cause);
674
+ if (e instanceof AggregateError && Array.isArray(e.errors)) for (const sub of e.errors) add(sub);
675
+ }
676
+ add(error);
677
+ return list;
678
+ }
679
+ function clientMessageFor(code) {
680
+ switch (code) {
681
+ case "NETWORK_DNS": return "Could not resolve authentication server hostname";
682
+ case "NETWORK_REFUSED": return "Connection refused by authentication server";
683
+ case "NETWORK_TIMEOUT": return "Authentication server connection timed out";
684
+ case "NETWORK_TLS": return "TLS error connecting to authentication server";
685
+ case "NETWORK_RESET": return "Connection to authentication server was reset";
686
+ case "NETWORK_ABORT": return "Authentication request was aborted";
687
+ default: return "Unable to connect to authentication server";
688
+ }
689
+ }
690
+ const INTERNAL_PUBLIC_MESSAGE = "Internal Server Error";
691
+ /**
692
+ * Inspects an error from `fetch` (including `cause` and aggregate errors) and
693
+ * returns a stable code for responses and observability.
694
+ */
695
+ function classifyFetchFailure(error) {
696
+ const nodes = collectRelatedErrors(error);
697
+ for (const node of nodes) if (isAbortError(node)) return {
698
+ kind: "transport",
699
+ code: "NETWORK_ABORT",
700
+ detail: "AbortError",
701
+ clientMessage: clientMessageFor("NETWORK_ABORT")
702
+ };
703
+ for (const node of nodes) {
704
+ const code = readErrnoCode(node);
705
+ const message = node instanceof Error ? node.message : "";
706
+ if (code === "ENOTFOUND" || code === "EAI_AGAIN") return {
707
+ kind: "transport",
708
+ code: "NETWORK_DNS",
709
+ detail: code,
710
+ clientMessage: clientMessageFor("NETWORK_DNS")
711
+ };
712
+ if (code === "ECONNREFUSED") return {
713
+ kind: "transport",
714
+ code: "NETWORK_REFUSED",
715
+ detail: code,
716
+ clientMessage: clientMessageFor("NETWORK_REFUSED")
717
+ };
718
+ if (code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT" || code === "UND_ERR_HEADERS_TIMEOUT" || code === "UND_ERR_BODY_TIMEOUT") return {
719
+ kind: "transport",
720
+ code: "NETWORK_TIMEOUT",
721
+ detail: code ?? "timeout",
722
+ clientMessage: clientMessageFor("NETWORK_TIMEOUT")
723
+ };
724
+ if (code === "ECONNRESET" || code === "EPIPE" || code === "ECONNABORTED") return {
725
+ kind: "transport",
726
+ code: "NETWORK_RESET",
727
+ detail: code,
728
+ clientMessage: clientMessageFor("NETWORK_RESET")
729
+ };
730
+ if (isTlsRelated(code, message)) return {
731
+ kind: "transport",
732
+ code: "NETWORK_TLS",
733
+ detail: code ?? "tls",
734
+ clientMessage: clientMessageFor("NETWORK_TLS")
735
+ };
736
+ }
737
+ for (const node of nodes) if (isLegacyFetchTypeError(node)) return {
738
+ kind: "transport",
739
+ code: "NETWORK_ERROR",
740
+ detail: "fetch TypeError",
741
+ clientMessage: clientMessageFor("NETWORK_ERROR")
742
+ };
743
+ const head = nodes[0];
744
+ return {
745
+ kind: "internal",
746
+ detail: (head instanceof Error ? head.message : INTERNAL_PUBLIC_MESSAGE).slice(0, 200),
747
+ clientMessage: INTERNAL_PUBLIC_MESSAGE
748
+ };
749
+ }
750
+
616
751
  //#endregion
617
752
  //#region src/server/client-factory.ts
618
753
  function createAuthServerInternal(config) {
619
- const { baseUrl, context: getContext, cookieSecret, sessionDataTtl, domain } = config;
754
+ const { baseUrl, context: getContext, cookieSecret, sessionDataTtl, domain, sameSite, log } = config;
755
+ const effectiveSameSite = sameSite ?? "strict";
620
756
  const fetchWithAuth = async (path, method, args) => {
621
757
  const ctx = await getContext();
622
758
  const cookies$1 = await ctx.getCookies();
@@ -638,10 +774,61 @@ function createAuthServerInternal(config) {
638
774
  headers$1["Content-Type"] = "application/json";
639
775
  requestBody = JSON.stringify(Object.keys(body).length > 0 ? body : {});
640
776
  }
641
- const response = await fetch(url.toString(), {
642
- method,
643
- headers: headers$1,
644
- body: requestBody
777
+ let response;
778
+ try {
779
+ response = await fetch(url.toString(), {
780
+ method,
781
+ headers: headers$1,
782
+ body: requestBody
783
+ });
784
+ } catch (error) {
785
+ const classified = classifyFetchFailure(error);
786
+ if (classified.kind === "transport") {
787
+ log?.warn("[neon-auth] Server API upstream fetch failed", {
788
+ component: "server-api",
789
+ path: url.pathname,
790
+ code: classified.code,
791
+ detail: classified.detail,
792
+ host: url.host
793
+ });
794
+ return {
795
+ data: null,
796
+ error: {
797
+ message: classified.clientMessage,
798
+ status: 502,
799
+ statusText: "Bad Gateway",
800
+ code: classified.code
801
+ }
802
+ };
803
+ }
804
+ log?.error("[neon-auth] Server API unexpected fetch error", {
805
+ component: "server-api",
806
+ path: url.pathname,
807
+ detail: classified.detail,
808
+ host: url.host,
809
+ err: error
810
+ });
811
+ throw error instanceof Error ? error : new Error(String(error));
812
+ }
813
+ if (response.ok) log?.debug("[neon-auth] Server API upstream fetch completed", {
814
+ component: "server-api",
815
+ path: url.pathname,
816
+ status: response.status,
817
+ host: url.host
818
+ });
819
+ else if (response.status >= 500) log?.warn("[neon-auth] Server API upstream HTTP error", {
820
+ component: "server-api",
821
+ path: url.pathname,
822
+ status: response.status,
823
+ statusText: response.statusText,
824
+ host: url.host
825
+ });
826
+ else log?.info("[neon-auth] Server API upstream HTTP non-2xx", {
827
+ component: "server-api",
828
+ path: url.pathname,
829
+ status: response.status,
830
+ statusText: response.statusText,
831
+ host: url.host
645
832
  });
646
833
  const setCookieHeaders = response.headers.getSetCookie();
647
834
  if (setCookieHeaders.length > 0) {
@@ -652,7 +839,7 @@ function createAuthServerInternal(config) {
652
839
  ...cookie,
653
840
  domain,
654
841
  partitioned: void 0,
655
- sameSite: "lax"
842
+ sameSite: effectiveSameSite
656
843
  };
657
844
  await ctx.setCookie(cookie.name, cookie.value, cookieOptions);
658
845
  }
@@ -661,25 +848,40 @@ function createAuthServerInternal(config) {
661
848
  const sessionDataCookie = await mintSessionDataFromResponse(response.headers, baseUrl, {
662
849
  secret: cookieSecret,
663
850
  sessionDataTtl,
664
- domain
851
+ domain,
852
+ sameSite
665
853
  });
666
854
  if (sessionDataCookie) {
667
855
  const [parsedSessionData] = parseSetCookies(sessionDataCookie);
668
856
  if (parsedSessionData) await ctx.setCookie(parsedSessionData.name, parsedSessionData.value, parsedSessionData);
669
857
  }
670
858
  } catch (error) {
671
- console.error("[fetchWithAuth] Failed to mint session data cookie:", error);
859
+ log?.warn("[neon-auth] Failed to mint session data cookie", {
860
+ component: "server-api",
861
+ detail: error instanceof Error ? error.message : String(error),
862
+ err: error
863
+ });
672
864
  }
673
865
  }
674
866
  const responseData = await response.json().catch(() => null);
675
- if (!response.ok) return {
676
- data: null,
677
- error: {
678
- message: responseData?.message || response.statusText,
867
+ if (!response.ok) {
868
+ const normalized = normalizeBetterAuthError({
679
869
  status: response.status,
680
- statusText: response.statusText
681
- }
682
- };
870
+ statusText: response.statusText,
871
+ message: responseData?.message || response.statusText,
872
+ code: responseData?.code,
873
+ body: responseData
874
+ });
875
+ return {
876
+ data: null,
877
+ error: {
878
+ message: normalized.message,
879
+ status: normalized.status ?? response.status,
880
+ statusText: response.statusText,
881
+ code: normalized.code
882
+ }
883
+ };
884
+ }
683
885
  return {
684
886
  data: responseData,
685
887
  error: null
@@ -701,7 +903,11 @@ function createAuthServerInternal(config) {
701
903
  };
702
904
  }
703
905
  } catch (error) {
704
- console.error("[auth.getSession] Cookie validation error:", error);
906
+ log?.warn("[neon-auth] Cookie validation error before getSession upstream call", {
907
+ component: "server-api",
908
+ detail: error instanceof Error ? error.message : String(error),
909
+ err: error
910
+ });
705
911
  }
706
912
  return originalGetSession(...args);
707
913
  };
@@ -726,6 +932,70 @@ function createApiProxy(endpoints, fetchFn) {
726
932
  });
727
933
  }
728
934
 
935
+ //#endregion
936
+ //#region src/server/logger.ts
937
+ const LEVEL_RANK = {
938
+ error: 0,
939
+ warn: 1,
940
+ info: 2,
941
+ debug: 3
942
+ };
943
+ const consoleSink = {
944
+ error: (message, meta) => {
945
+ if (meta && Object.keys(meta).length > 0) console.error(message, meta);
946
+ else console.error(message);
947
+ },
948
+ warn: (message, meta) => {
949
+ if (meta && Object.keys(meta).length > 0) console.warn(message, meta);
950
+ else console.warn(message);
951
+ },
952
+ info: (message, meta) => {
953
+ if (meta && Object.keys(meta).length > 0) console.info(message, meta);
954
+ else console.info(message);
955
+ },
956
+ debug: (message, meta) => {
957
+ if (meta && Object.keys(meta).length > 0) console.debug(message, meta);
958
+ else console.debug(message);
959
+ }
960
+ };
961
+ function wrapWithLevel(level, logger) {
962
+ const minRank = LEVEL_RANK[level];
963
+ const gate = (messageLevel, fn) => {
964
+ return (message, meta) => {
965
+ if (LEVEL_RANK[messageLevel] <= minRank) fn(message, meta);
966
+ };
967
+ };
968
+ return {
969
+ error: gate("error", logger.error),
970
+ warn: gate("warn", logger.warn),
971
+ info: gate("info", logger.info),
972
+ debug: gate("debug", logger.debug)
973
+ };
974
+ }
975
+ const noopResolved = {
976
+ error: () => {},
977
+ warn: () => {},
978
+ info: () => {},
979
+ debug: () => {}
980
+ };
981
+ /**
982
+ * Merges user logger with `console`, applies {@link NeonAuthLoggingInput} level rules.
983
+ *
984
+ * **Opt-out:** Defaults to `warn` (structured `error` / `warn` to `console`). Set **`logLevel: 'silent'`**
985
+ * to disable completely. Custom **`logger`** overrides `console` per level when not silent.
986
+ */
987
+ function resolveNeonAuthLogging(input) {
988
+ if (input?.logLevel === "silent") return noopResolved;
989
+ const level = input?.logLevel ?? "warn";
990
+ const raw = input?.logger ?? {};
991
+ return wrapWithLevel(level, {
992
+ error: raw.error ?? consoleSink.error,
993
+ warn: raw.warn ?? consoleSink.warn,
994
+ info: raw.info ?? consoleSink.info,
995
+ debug: raw.debug ?? consoleSink.debug
996
+ });
997
+ }
998
+
729
999
  //#endregion
730
1000
  //#region src/next/server/adapter.ts
731
1001
  /**
@@ -783,36 +1053,80 @@ const PROXY_HEADERS = [
783
1053
  * This is framework-agnostic and can be used by any server framework
784
1054
  */
785
1055
  const NEON_AUTH_HEADER_MIDDLEWARE_NAME = "x-neon-auth-middleware";
1056
+ function safeAuthHost(baseUrl) {
1057
+ try {
1058
+ return new URL(baseUrl).host;
1059
+ } catch {
1060
+ return;
1061
+ }
1062
+ }
786
1063
  /**
787
1064
  * Handles proxying authentication requests to the upstream Neon Auth server
788
1065
  *
789
1066
  * @param baseUrl - Base URL of the Neon Auth server
790
1067
  * @param request - Standard Web API Request object
791
1068
  * @param path - API path to proxy to (e.g., 'get-session', 'sign-in')
1069
+ * @param log - Optional resolved logging sink
792
1070
  * @returns Response from upstream server or error response
793
1071
  */
794
- const handleAuthRequest = async (baseUrl, request, path) => {
1072
+ const handleAuthRequest = async (baseUrl, request, path, log) => {
795
1073
  const headers$1 = prepareRequestHeaders(request);
796
1074
  const body = await parseRequestBody(request);
797
1075
  try {
798
1076
  const upstreamURL = getUpstreamURL(baseUrl, path, { originalUrl: new URL(request.url) });
799
- return await fetch(upstreamURL.toString(), {
1077
+ const response = await fetch(upstreamURL.toString(), {
800
1078
  method: request.method,
801
1079
  headers: headers$1,
802
1080
  body
803
1081
  });
1082
+ const host = safeAuthHost(baseUrl);
1083
+ if (response.ok) log?.debug("[neon-auth] Upstream fetch completed", {
1084
+ component: "proxy",
1085
+ proxyPath: path,
1086
+ status: response.status,
1087
+ host
1088
+ });
1089
+ else if (response.status >= 500) log?.warn("[neon-auth] Upstream HTTP error", {
1090
+ component: "proxy",
1091
+ proxyPath: path,
1092
+ status: response.status,
1093
+ statusText: response.statusText,
1094
+ host
1095
+ });
1096
+ else log?.info("[neon-auth] Upstream HTTP non-2xx", {
1097
+ component: "proxy",
1098
+ proxyPath: path,
1099
+ status: response.status,
1100
+ statusText: response.statusText,
1101
+ host
1102
+ });
1103
+ return response;
804
1104
  } catch (error) {
805
- if (error instanceof Error && error.name === "TypeError" && error.message.includes("fetch")) return Response.json({
806
- error: "Unable to connect to authentication server",
807
- code: "NETWORK_ERROR"
808
- }, {
809
- status: 502,
810
- headers: { "Content-Type": "application/json" }
1105
+ const classified = classifyFetchFailure(error);
1106
+ if (classified.kind === "transport") {
1107
+ log?.warn("[neon-auth] Upstream fetch failed", {
1108
+ component: "proxy",
1109
+ proxyPath: path,
1110
+ code: classified.code,
1111
+ detail: classified.detail,
1112
+ host: safeAuthHost(baseUrl)
1113
+ });
1114
+ return Response.json({
1115
+ error: classified.clientMessage,
1116
+ code: classified.code
1117
+ }, {
1118
+ status: 502,
1119
+ headers: { "Content-Type": "application/json" }
1120
+ });
1121
+ }
1122
+ log?.error("[neon-auth] Unexpected proxy error", {
1123
+ component: "proxy",
1124
+ proxyPath: path,
1125
+ detail: classified.detail,
1126
+ host: safeAuthHost(baseUrl)
811
1127
  });
812
- const message = error instanceof Error ? error.message : "Internal Server Error";
813
- console.error(`[AuthError] ${message}`, error);
814
1128
  return Response.json({
815
- error: message,
1129
+ error: classified.clientMessage,
816
1130
  code: "INTERNAL_ERROR"
817
1131
  }, {
818
1132
  status: 500,
@@ -876,7 +1190,7 @@ const RESPONSE_HEADERS_ALLOWLIST = [
876
1190
  * @returns New Response with proxied headers and session data cookie
877
1191
  */
878
1192
  const handleAuthResponse = async (response, baseUrl, cookieConfig) => {
879
- const responseHeaders = prepareResponseHeaders(response, cookieConfig.domain);
1193
+ const responseHeaders = prepareResponseHeaders(response, cookieConfig);
880
1194
  const sessionDataCookie = await mintSessionDataFromResponse(response.headers, baseUrl, cookieConfig);
881
1195
  if (sessionDataCookie) responseHeaders.append("Set-Cookie", sessionDataCookie);
882
1196
  return new Response(response.body, {
@@ -885,15 +1199,17 @@ const handleAuthResponse = async (response, baseUrl, cookieConfig) => {
885
1199
  headers: responseHeaders
886
1200
  });
887
1201
  };
888
- const prepareResponseHeaders = (response, domain) => {
1202
+ const prepareResponseHeaders = (response, cookieConfig) => {
889
1203
  const headers$1 = new Headers();
1204
+ const effectiveSameSite = cookieConfig.sameSite ?? "strict";
1205
+ const { domain } = cookieConfig;
890
1206
  for (const header of RESPONSE_HEADERS_ALLOWLIST) if (header === "set-cookie") {
891
1207
  const cookies$1 = response.headers.getSetCookie();
892
1208
  for (const cookieHeader of cookies$1) {
893
1209
  const parsedCookies = parseSetCookies(cookieHeader);
894
1210
  for (const parsedCookie of parsedCookies) {
895
1211
  parsedCookie.partitioned = void 0;
896
- parsedCookie.sameSite = "lax";
1212
+ parsedCookie.sameSite = effectiveSameSite;
897
1213
  if (domain) parsedCookie.domain = domain;
898
1214
  headers$1.append("Set-Cookie", serializeSetCookie(parsedCookie));
899
1215
  }
@@ -992,19 +1308,21 @@ function extractSessionTokenCookie(cookieHeader) {
992
1308
  * @returns Standard Web API Response
993
1309
  */
994
1310
  async function handleAuthProxyRequest(config) {
995
- const { request, path, baseUrl, cookieSecret, sessionDataTtl, domain } = config;
1311
+ const { request, path, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite, log } = config;
996
1312
  if (path === API_ENDPOINTS.getSession.path && request.method === API_ENDPOINTS.getSession.method) {
997
1313
  const cachedResponse = await trySessionCache(request, baseUrl, {
998
1314
  secret: cookieSecret,
999
1315
  sessionDataTtl,
1000
- domain
1316
+ domain,
1317
+ sameSite
1001
1318
  });
1002
1319
  if (cachedResponse) return cachedResponse;
1003
1320
  }
1004
- return await handleAuthResponse(await handleAuthRequest(baseUrl, request, path), baseUrl, {
1321
+ return await handleAuthResponse(await handleAuthRequest(baseUrl, request, path, log), baseUrl, {
1005
1322
  secret: cookieSecret,
1006
1323
  sessionDataTtl,
1007
- domain
1324
+ domain,
1325
+ sameSite
1008
1326
  });
1009
1327
  }
1010
1328
 
@@ -1040,6 +1358,7 @@ async function handleAuthProxyRequest(config) {
1040
1358
  function authApiHandler(config) {
1041
1359
  const { baseUrl, cookies: cookies$1 } = config;
1042
1360
  validateCookieConfig(cookies$1);
1361
+ const log = resolveNeonAuthLogging(config);
1043
1362
  const handler = async (request, { params }) => {
1044
1363
  return handleAuthProxyRequest({
1045
1364
  request,
@@ -1047,7 +1366,9 @@ function authApiHandler(config) {
1047
1366
  baseUrl,
1048
1367
  cookieSecret: cookies$1.secret,
1049
1368
  sessionDataTtl: cookies$1.sessionDataTtl,
1050
- domain: cookies$1.domain
1369
+ domain: cookies$1.domain,
1370
+ sameSite: cookies$1.sameSite,
1371
+ log
1051
1372
  });
1052
1373
  };
1053
1374
  return {
@@ -1084,9 +1405,11 @@ function needsSessionVerification(request) {
1084
1405
  * @param cookieSecret - Secret for signing session cookies
1085
1406
  * @param sessionDataTtl - Optional TTL for session data cache
1086
1407
  * @param domain - Optional cookie domain
1408
+ * @param sameSite - SameSite for cookies set during exchange
1409
+ * @param log - Resolved Neon Auth logging sink (proxy / middleware)
1087
1410
  * @returns Exchange result with redirect URL and cookies, or null if exchange not needed/failed
1088
1411
  */
1089
- async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain) {
1412
+ async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite, log) {
1090
1413
  const url = new URL(request.url);
1091
1414
  const verifier = url.searchParams.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
1092
1415
  const cookieHeader = request.headers.get("cookie");
@@ -1096,10 +1419,11 @@ async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl
1096
1419
  const response = await handleAuthResponse(await handleAuthRequest(baseUrl, new Request(request.url, {
1097
1420
  method: "GET",
1098
1421
  headers: request.headers
1099
- }), "get-session"), baseUrl, {
1422
+ }), "get-session", log), baseUrl, {
1100
1423
  secret: cookieSecret,
1101
1424
  sessionDataTtl,
1102
- domain
1425
+ domain,
1426
+ sameSite
1103
1427
  });
1104
1428
  if (response.ok) {
1105
1429
  const setCookieHeaders = response.headers.getSetCookie();
@@ -1174,10 +1498,15 @@ function checkSessionRequired(pathname, skipRoutes, loginUrl, session) {
1174
1498
  * @returns Decision object indicating what action to take
1175
1499
  */
1176
1500
  async function processAuthMiddleware(config) {
1177
- const { request, pathname, skipRoutes, loginUrl, baseUrl, cookieSecret, sessionDataTtl, domain } = config;
1501
+ const log = config.log ?? resolveNeonAuthLogging({
1502
+ logger: config.logger,
1503
+ logLevel: config.logLevel
1504
+ });
1505
+ const { request, pathname, skipRoutes, loginUrl, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite } = config;
1506
+ const effectiveSameSite = sameSite ?? "strict";
1178
1507
  if (pathname.startsWith(loginUrl)) return { action: "allow" };
1179
1508
  if (needsSessionVerification(request)) {
1180
- const exchangeResult = await exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain);
1509
+ const exchangeResult = await exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite, log);
1181
1510
  if (exchangeResult !== null) return {
1182
1511
  action: "redirect_oauth",
1183
1512
  redirectUrl: exchangeResult.redirectUrl,
@@ -1199,10 +1528,18 @@ async function processAuthMiddleware(config) {
1199
1528
  baseUrl,
1200
1529
  cookieSecret,
1201
1530
  sessionDataTtl,
1202
- domain
1531
+ domain,
1532
+ sameSite,
1533
+ log
1203
1534
  });
1204
1535
  if (sessionResponse.ok) {
1205
- const data = await sessionResponse.json().catch(() => null);
1536
+ const data = await sessionResponse.json().catch((error) => {
1537
+ log.debug("[neon-auth] Failed to parse session response JSON", {
1538
+ component: "middleware",
1539
+ detail: error instanceof Error ? error.message : String(error)
1540
+ });
1541
+ return null;
1542
+ });
1206
1543
  if (data) sessionData = data;
1207
1544
  }
1208
1545
  sessionCookies = sessionResponse.headers.getSetCookie();
@@ -1220,7 +1557,7 @@ async function processAuthMiddleware(config) {
1220
1557
  domain,
1221
1558
  httpOnly: true,
1222
1559
  secure: true,
1223
- sameSite: "lax",
1560
+ sameSite: effectiveSameSite,
1224
1561
  maxAge: 0
1225
1562
  }));
1226
1563
  return {
@@ -1249,6 +1586,9 @@ const SKIP_ROUTES = [
1249
1586
  * @param config.cookies - Cookie configuration
1250
1587
  * @param config.cookies.secret - Secret for signing session cookies (minimum 32 characters)
1251
1588
  * @param config.cookies.sessionDataTtl - Optional TTL for session cache in seconds (default: 300)
1589
+ * @param config.logger - Optional structured logger; omitted methods fall back to `console`
1590
+ * @param config.logLevel - Minimum log level; `'silent'` disables Neon Auth console output (default: `warn`)
1591
+ * @param config.log - Pre-resolved logging sink (set by {@link createNeonAuth})
1252
1592
  * @param config.loginUrl - The URL to redirect to when the user is not authenticated (default: '/auth/sign-in')
1253
1593
  * @returns A middleware function that can be used in the Next.js app.
1254
1594
  * @throws Error if `cookies.secret` is less than 32 characters
@@ -1267,7 +1607,7 @@ const SKIP_ROUTES = [
1267
1607
  * ```
1268
1608
  */
1269
1609
  function neonAuthMiddleware(config) {
1270
- const { baseUrl, cookies: cookies$1, loginUrl = "/auth/sign-in" } = config;
1610
+ const { baseUrl, cookies: cookies$1, loginUrl = "/auth/sign-in", log, logger, logLevel } = config;
1271
1611
  validateCookieConfig(cookies$1);
1272
1612
  return async (request) => {
1273
1613
  const pathname = request.nextUrl.pathname;
@@ -1279,7 +1619,11 @@ function neonAuthMiddleware(config) {
1279
1619
  baseUrl,
1280
1620
  cookieSecret: cookies$1.secret,
1281
1621
  sessionDataTtl: cookies$1.sessionDataTtl,
1282
- domain: cookies$1.domain
1622
+ domain: cookies$1.domain,
1623
+ sameSite: cookies$1.sameSite,
1624
+ log,
1625
+ logger,
1626
+ logLevel
1283
1627
  });
1284
1628
  switch (result.action) {
1285
1629
  case "allow": {
@@ -1330,6 +1674,8 @@ function neonAuthMiddleware(config) {
1330
1674
  * @param config.cookies.secret - Secret for signing session cookies (minimum 32 characters)
1331
1675
  * @param config.cookies.sessionDataTtl - Optional TTL for session cache in seconds (default: 300)
1332
1676
  * @param config.cookies.domain - Optional cookie domain (default: current domain)
1677
+ * @param config.logger - Optional structured logger; omitted methods fall back to `console` (see {@link NeonAuthLogger})
1678
+ * @param config.logLevel - Minimum level; `'silent'` disables Neon Auth server console logs (default: `warn`)
1333
1679
  * @returns Unified auth instance with server methods, handler, and middleware
1334
1680
  * @throws Error if `cookies.secret` is less than 32 characters
1335
1681
  *
@@ -1402,12 +1748,15 @@ function neonAuthMiddleware(config) {
1402
1748
  function createNeonAuth(config) {
1403
1749
  const { baseUrl, cookies: cookies$1 } = config;
1404
1750
  validateCookieConfig(cookies$1);
1751
+ const log = resolveNeonAuthLogging(config);
1405
1752
  const server = createAuthServerInternal({
1406
1753
  baseUrl,
1407
1754
  context: createNextRequestContext,
1408
1755
  cookieSecret: cookies$1.secret,
1409
1756
  sessionDataTtl: cookies$1.sessionDataTtl,
1410
- domain: cookies$1.domain
1757
+ domain: cookies$1.domain,
1758
+ sameSite: cookies$1.sameSite,
1759
+ log
1411
1760
  });
1412
1761
  /**
1413
1762
  * Creates API route handlers for Next.js
@@ -1452,10 +1801,11 @@ function createNeonAuth(config) {
1452
1801
  */
1453
1802
  server.middleware = (middlewareConfig) => neonAuthMiddleware({
1454
1803
  ...config,
1455
- ...middlewareConfig
1804
+ ...middlewareConfig,
1805
+ log
1456
1806
  });
1457
1807
  return server;
1458
1808
  }
1459
1809
 
1460
1810
  //#endregion
1461
- export { createNeonAuth };
1811
+ export { AuthApiError, AuthError, NEON_AUTH_NETWORK_ERROR_CODES, classifyFetchFailure, createNeonAuth, isAuthApiError, isAuthError, resolveNeonAuthLogging };