@neondatabase/auth 0.4.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.
- package/dist/{adapter-core-Bt4M5I2g.mjs → adapter-core-BFMM3lwe.mjs} +1 -1
- package/dist/{adapter-core-BWM7cWOp.d.mts → adapter-core-ClY-p_AI.d.mts} +159 -159
- package/dist/{better-auth-react-adapter-aMv8WeDb.mjs → better-auth-react-adapter-DZTZVVnk.mjs} +1 -1
- package/dist/{supabase-adapter-BGwV0Vu2.d.mts → better-auth-react-adapter-iJMZCLUI.d.mts} +367 -424
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{neon-auth-CS4FpK2X.mjs → neon-auth-VDrC3GwX.mjs} +1 -1
- package/dist/next/index.mjs +3 -3
- package/dist/next/server/index.d.mts +108 -13
- package/dist/next/server/index.mjs +351 -31
- package/dist/react/adapters/index.d.mts +2 -2
- package/dist/react/adapters/index.mjs +2 -2
- package/dist/react/index.d.mts +2 -2
- package/dist/react/index.mjs +2 -2
- package/dist/{supabase-adapter-DBt4LJJd.mjs → supabase-adapter-CAyBFrNn.mjs} +1 -1
- package/dist/{better-auth-react-adapter-BDxJ65mF.d.mts → supabase-adapter-cuLnmLDs.d.mts} +176 -119
- package/dist/types/index.d.mts +1 -1
- package/dist/vanilla/adapters/index.d.mts +2 -2
- package/dist/vanilla/adapters/index.mjs +2 -2
- package/dist/vanilla/index.d.mts +2 -2
- package/dist/vanilla/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -621,10 +621,137 @@ async function mintSessionDataFromToken(sessionTokenCookie, baseUrl, cookieConfi
|
|
|
621
621
|
return await mintSessionDataCookie(sessionTokenCookie, baseUrl, cookieConfig);
|
|
622
622
|
}
|
|
623
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
|
+
|
|
624
751
|
//#endregion
|
|
625
752
|
//#region src/server/client-factory.ts
|
|
626
753
|
function createAuthServerInternal(config) {
|
|
627
|
-
const { baseUrl, context: getContext, cookieSecret, sessionDataTtl, domain, sameSite } = config;
|
|
754
|
+
const { baseUrl, context: getContext, cookieSecret, sessionDataTtl, domain, sameSite, log } = config;
|
|
628
755
|
const effectiveSameSite = sameSite ?? "strict";
|
|
629
756
|
const fetchWithAuth = async (path, method, args) => {
|
|
630
757
|
const ctx = await getContext();
|
|
@@ -647,10 +774,61 @@ function createAuthServerInternal(config) {
|
|
|
647
774
|
headers$1["Content-Type"] = "application/json";
|
|
648
775
|
requestBody = JSON.stringify(Object.keys(body).length > 0 ? body : {});
|
|
649
776
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
|
654
832
|
});
|
|
655
833
|
const setCookieHeaders = response.headers.getSetCookie();
|
|
656
834
|
if (setCookieHeaders.length > 0) {
|
|
@@ -678,7 +856,11 @@ function createAuthServerInternal(config) {
|
|
|
678
856
|
if (parsedSessionData) await ctx.setCookie(parsedSessionData.name, parsedSessionData.value, parsedSessionData);
|
|
679
857
|
}
|
|
680
858
|
} catch (error) {
|
|
681
|
-
|
|
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
|
+
});
|
|
682
864
|
}
|
|
683
865
|
}
|
|
684
866
|
const responseData = await response.json().catch(() => null);
|
|
@@ -721,7 +903,11 @@ function createAuthServerInternal(config) {
|
|
|
721
903
|
};
|
|
722
904
|
}
|
|
723
905
|
} catch (error) {
|
|
724
|
-
|
|
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
|
+
});
|
|
725
911
|
}
|
|
726
912
|
return originalGetSession(...args);
|
|
727
913
|
};
|
|
@@ -746,6 +932,70 @@ function createApiProxy(endpoints, fetchFn) {
|
|
|
746
932
|
});
|
|
747
933
|
}
|
|
748
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
|
+
|
|
749
999
|
//#endregion
|
|
750
1000
|
//#region src/next/server/adapter.ts
|
|
751
1001
|
/**
|
|
@@ -803,36 +1053,80 @@ const PROXY_HEADERS = [
|
|
|
803
1053
|
* This is framework-agnostic and can be used by any server framework
|
|
804
1054
|
*/
|
|
805
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
|
+
}
|
|
806
1063
|
/**
|
|
807
1064
|
* Handles proxying authentication requests to the upstream Neon Auth server
|
|
808
1065
|
*
|
|
809
1066
|
* @param baseUrl - Base URL of the Neon Auth server
|
|
810
1067
|
* @param request - Standard Web API Request object
|
|
811
1068
|
* @param path - API path to proxy to (e.g., 'get-session', 'sign-in')
|
|
1069
|
+
* @param log - Optional resolved logging sink
|
|
812
1070
|
* @returns Response from upstream server or error response
|
|
813
1071
|
*/
|
|
814
|
-
const handleAuthRequest = async (baseUrl, request, path) => {
|
|
1072
|
+
const handleAuthRequest = async (baseUrl, request, path, log) => {
|
|
815
1073
|
const headers$1 = prepareRequestHeaders(request);
|
|
816
1074
|
const body = await parseRequestBody(request);
|
|
817
1075
|
try {
|
|
818
1076
|
const upstreamURL = getUpstreamURL(baseUrl, path, { originalUrl: new URL(request.url) });
|
|
819
|
-
|
|
1077
|
+
const response = await fetch(upstreamURL.toString(), {
|
|
820
1078
|
method: request.method,
|
|
821
1079
|
headers: headers$1,
|
|
822
1080
|
body
|
|
823
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;
|
|
824
1104
|
} catch (error) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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)
|
|
831
1127
|
});
|
|
832
|
-
const message = error instanceof Error ? error.message : "Internal Server Error";
|
|
833
|
-
console.error(`[AuthError] ${message}`, error);
|
|
834
1128
|
return Response.json({
|
|
835
|
-
error:
|
|
1129
|
+
error: classified.clientMessage,
|
|
836
1130
|
code: "INTERNAL_ERROR"
|
|
837
1131
|
}, {
|
|
838
1132
|
status: 500,
|
|
@@ -1014,7 +1308,7 @@ function extractSessionTokenCookie(cookieHeader) {
|
|
|
1014
1308
|
* @returns Standard Web API Response
|
|
1015
1309
|
*/
|
|
1016
1310
|
async function handleAuthProxyRequest(config) {
|
|
1017
|
-
const { request, path, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite } = config;
|
|
1311
|
+
const { request, path, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite, log } = config;
|
|
1018
1312
|
if (path === API_ENDPOINTS.getSession.path && request.method === API_ENDPOINTS.getSession.method) {
|
|
1019
1313
|
const cachedResponse = await trySessionCache(request, baseUrl, {
|
|
1020
1314
|
secret: cookieSecret,
|
|
@@ -1024,7 +1318,7 @@ async function handleAuthProxyRequest(config) {
|
|
|
1024
1318
|
});
|
|
1025
1319
|
if (cachedResponse) return cachedResponse;
|
|
1026
1320
|
}
|
|
1027
|
-
return await handleAuthResponse(await handleAuthRequest(baseUrl, request, path), baseUrl, {
|
|
1321
|
+
return await handleAuthResponse(await handleAuthRequest(baseUrl, request, path, log), baseUrl, {
|
|
1028
1322
|
secret: cookieSecret,
|
|
1029
1323
|
sessionDataTtl,
|
|
1030
1324
|
domain,
|
|
@@ -1064,6 +1358,7 @@ async function handleAuthProxyRequest(config) {
|
|
|
1064
1358
|
function authApiHandler(config) {
|
|
1065
1359
|
const { baseUrl, cookies: cookies$1 } = config;
|
|
1066
1360
|
validateCookieConfig(cookies$1);
|
|
1361
|
+
const log = resolveNeonAuthLogging(config);
|
|
1067
1362
|
const handler = async (request, { params }) => {
|
|
1068
1363
|
return handleAuthProxyRequest({
|
|
1069
1364
|
request,
|
|
@@ -1072,7 +1367,8 @@ function authApiHandler(config) {
|
|
|
1072
1367
|
cookieSecret: cookies$1.secret,
|
|
1073
1368
|
sessionDataTtl: cookies$1.sessionDataTtl,
|
|
1074
1369
|
domain: cookies$1.domain,
|
|
1075
|
-
sameSite: cookies$1.sameSite
|
|
1370
|
+
sameSite: cookies$1.sameSite,
|
|
1371
|
+
log
|
|
1076
1372
|
});
|
|
1077
1373
|
};
|
|
1078
1374
|
return {
|
|
@@ -1109,9 +1405,11 @@ function needsSessionVerification(request) {
|
|
|
1109
1405
|
* @param cookieSecret - Secret for signing session cookies
|
|
1110
1406
|
* @param sessionDataTtl - Optional TTL for session data cache
|
|
1111
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)
|
|
1112
1410
|
* @returns Exchange result with redirect URL and cookies, or null if exchange not needed/failed
|
|
1113
1411
|
*/
|
|
1114
|
-
async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite) {
|
|
1412
|
+
async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite, log) {
|
|
1115
1413
|
const url = new URL(request.url);
|
|
1116
1414
|
const verifier = url.searchParams.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
|
|
1117
1415
|
const cookieHeader = request.headers.get("cookie");
|
|
@@ -1121,7 +1419,7 @@ async function exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl
|
|
|
1121
1419
|
const response = await handleAuthResponse(await handleAuthRequest(baseUrl, new Request(request.url, {
|
|
1122
1420
|
method: "GET",
|
|
1123
1421
|
headers: request.headers
|
|
1124
|
-
}), "get-session"), baseUrl, {
|
|
1422
|
+
}), "get-session", log), baseUrl, {
|
|
1125
1423
|
secret: cookieSecret,
|
|
1126
1424
|
sessionDataTtl,
|
|
1127
1425
|
domain,
|
|
@@ -1200,11 +1498,15 @@ function checkSessionRequired(pathname, skipRoutes, loginUrl, session) {
|
|
|
1200
1498
|
* @returns Decision object indicating what action to take
|
|
1201
1499
|
*/
|
|
1202
1500
|
async function processAuthMiddleware(config) {
|
|
1501
|
+
const log = config.log ?? resolveNeonAuthLogging({
|
|
1502
|
+
logger: config.logger,
|
|
1503
|
+
logLevel: config.logLevel
|
|
1504
|
+
});
|
|
1203
1505
|
const { request, pathname, skipRoutes, loginUrl, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite } = config;
|
|
1204
1506
|
const effectiveSameSite = sameSite ?? "strict";
|
|
1205
1507
|
if (pathname.startsWith(loginUrl)) return { action: "allow" };
|
|
1206
1508
|
if (needsSessionVerification(request)) {
|
|
1207
|
-
const exchangeResult = await exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite);
|
|
1509
|
+
const exchangeResult = await exchangeOAuthToken(request, baseUrl, cookieSecret, sessionDataTtl, domain, sameSite, log);
|
|
1208
1510
|
if (exchangeResult !== null) return {
|
|
1209
1511
|
action: "redirect_oauth",
|
|
1210
1512
|
redirectUrl: exchangeResult.redirectUrl,
|
|
@@ -1227,10 +1529,17 @@ async function processAuthMiddleware(config) {
|
|
|
1227
1529
|
cookieSecret,
|
|
1228
1530
|
sessionDataTtl,
|
|
1229
1531
|
domain,
|
|
1230
|
-
sameSite
|
|
1532
|
+
sameSite,
|
|
1533
|
+
log
|
|
1231
1534
|
});
|
|
1232
1535
|
if (sessionResponse.ok) {
|
|
1233
|
-
const data = await sessionResponse.json().catch(() =>
|
|
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
|
+
});
|
|
1234
1543
|
if (data) sessionData = data;
|
|
1235
1544
|
}
|
|
1236
1545
|
sessionCookies = sessionResponse.headers.getSetCookie();
|
|
@@ -1277,6 +1586,9 @@ const SKIP_ROUTES = [
|
|
|
1277
1586
|
* @param config.cookies - Cookie configuration
|
|
1278
1587
|
* @param config.cookies.secret - Secret for signing session cookies (minimum 32 characters)
|
|
1279
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})
|
|
1280
1592
|
* @param config.loginUrl - The URL to redirect to when the user is not authenticated (default: '/auth/sign-in')
|
|
1281
1593
|
* @returns A middleware function that can be used in the Next.js app.
|
|
1282
1594
|
* @throws Error if `cookies.secret` is less than 32 characters
|
|
@@ -1295,7 +1607,7 @@ const SKIP_ROUTES = [
|
|
|
1295
1607
|
* ```
|
|
1296
1608
|
*/
|
|
1297
1609
|
function neonAuthMiddleware(config) {
|
|
1298
|
-
const { baseUrl, cookies: cookies$1, loginUrl = "/auth/sign-in" } = config;
|
|
1610
|
+
const { baseUrl, cookies: cookies$1, loginUrl = "/auth/sign-in", log, logger, logLevel } = config;
|
|
1299
1611
|
validateCookieConfig(cookies$1);
|
|
1300
1612
|
return async (request) => {
|
|
1301
1613
|
const pathname = request.nextUrl.pathname;
|
|
@@ -1308,7 +1620,10 @@ function neonAuthMiddleware(config) {
|
|
|
1308
1620
|
cookieSecret: cookies$1.secret,
|
|
1309
1621
|
sessionDataTtl: cookies$1.sessionDataTtl,
|
|
1310
1622
|
domain: cookies$1.domain,
|
|
1311
|
-
sameSite: cookies$1.sameSite
|
|
1623
|
+
sameSite: cookies$1.sameSite,
|
|
1624
|
+
log,
|
|
1625
|
+
logger,
|
|
1626
|
+
logLevel
|
|
1312
1627
|
});
|
|
1313
1628
|
switch (result.action) {
|
|
1314
1629
|
case "allow": {
|
|
@@ -1359,6 +1674,8 @@ function neonAuthMiddleware(config) {
|
|
|
1359
1674
|
* @param config.cookies.secret - Secret for signing session cookies (minimum 32 characters)
|
|
1360
1675
|
* @param config.cookies.sessionDataTtl - Optional TTL for session cache in seconds (default: 300)
|
|
1361
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`)
|
|
1362
1679
|
* @returns Unified auth instance with server methods, handler, and middleware
|
|
1363
1680
|
* @throws Error if `cookies.secret` is less than 32 characters
|
|
1364
1681
|
*
|
|
@@ -1431,13 +1748,15 @@ function neonAuthMiddleware(config) {
|
|
|
1431
1748
|
function createNeonAuth(config) {
|
|
1432
1749
|
const { baseUrl, cookies: cookies$1 } = config;
|
|
1433
1750
|
validateCookieConfig(cookies$1);
|
|
1751
|
+
const log = resolveNeonAuthLogging(config);
|
|
1434
1752
|
const server = createAuthServerInternal({
|
|
1435
1753
|
baseUrl,
|
|
1436
1754
|
context: createNextRequestContext,
|
|
1437
1755
|
cookieSecret: cookies$1.secret,
|
|
1438
1756
|
sessionDataTtl: cookies$1.sessionDataTtl,
|
|
1439
1757
|
domain: cookies$1.domain,
|
|
1440
|
-
sameSite: cookies$1.sameSite
|
|
1758
|
+
sameSite: cookies$1.sameSite,
|
|
1759
|
+
log
|
|
1441
1760
|
});
|
|
1442
1761
|
/**
|
|
1443
1762
|
* Creates API route handlers for Next.js
|
|
@@ -1482,10 +1801,11 @@ function createNeonAuth(config) {
|
|
|
1482
1801
|
*/
|
|
1483
1802
|
server.middleware = (middlewareConfig) => neonAuthMiddleware({
|
|
1484
1803
|
...config,
|
|
1485
|
-
...middlewareConfig
|
|
1804
|
+
...middlewareConfig,
|
|
1805
|
+
log
|
|
1486
1806
|
});
|
|
1487
1807
|
return server;
|
|
1488
1808
|
}
|
|
1489
1809
|
|
|
1490
1810
|
//#endregion
|
|
1491
|
-
export { AuthApiError, AuthError, createNeonAuth, isAuthApiError, isAuthError };
|
|
1811
|
+
export { AuthApiError, AuthError, NEON_AUTH_NETWORK_ERROR_CODES, classifyFetchFailure, createNeonAuth, isAuthApiError, isAuthError, resolveNeonAuthLogging };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../../adapter-core-
|
|
2
|
-
import { n as BetterAuthReactAdapterInstance, r as BetterAuthReactAdapterOptions, t as BetterAuthReactAdapter } from "../../better-auth-react-adapter-
|
|
1
|
+
import "../../adapter-core-ClY-p_AI.mjs";
|
|
2
|
+
import { n as BetterAuthReactAdapterInstance, r as BetterAuthReactAdapterOptions, t as BetterAuthReactAdapter } from "../../better-auth-react-adapter-iJMZCLUI.mjs";
|
|
3
3
|
import "../../index-B0Pd4HOH.mjs";
|
|
4
4
|
export { BetterAuthReactAdapter, BetterAuthReactAdapterInstance, BetterAuthReactAdapterOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../../adapter-core-
|
|
2
|
-
import { t as BetterAuthReactAdapter } from "../../better-auth-react-adapter-
|
|
1
|
+
import "../../adapter-core-BFMM3lwe.mjs";
|
|
2
|
+
import { t as BetterAuthReactAdapter } from "../../better-auth-react-adapter-DZTZVVnk.mjs";
|
|
3
3
|
|
|
4
4
|
export { BetterAuthReactAdapter };
|
package/dist/react/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "../adapter-core-
|
|
2
|
-
import { n as BetterAuthReactAdapterInstance, r as BetterAuthReactAdapterOptions, t as BetterAuthReactAdapter } from "../better-auth-react-adapter-
|
|
1
|
+
import "../adapter-core-ClY-p_AI.mjs";
|
|
2
|
+
import { n as BetterAuthReactAdapterInstance, r as BetterAuthReactAdapterOptions, t as BetterAuthReactAdapter } from "../better-auth-react-adapter-iJMZCLUI.mjs";
|
|
3
3
|
import "../index-B0Pd4HOH.mjs";
|
|
4
4
|
import { $ as MagicLinkFormProps, $t as SignUpFormProps, A as ChangePasswordCard, An as UserViewClassNames, At as PasswordInput, B as DropboxIcon, Bn as socialProviders, Bt as ResetPasswordFormProps, C as AuthUIProviderProps, Cn as UserAvatarClassNames, Ct as OrganizationViewPageProps, D as AuthViewPaths, Dn as UserButtonProps, Dt as OrganizationsCard, E as AuthViewPath, En as UserButtonClassNames, Et as OrganizationViewProps, F as CreateTeamDialogProps, Fn as accountViewPaths, Ft as RecoverAccountFormProps, G as GitLabIcon, Gt as SettingsCard, H as ForgotPasswordForm, Hn as useAuthenticate, Ht as SecuritySettingsCards, I as DeleteAccountCard, In as authLocalization, It as RedditIcon, J as InputFieldSkeleton, Jt as SettingsCellSkeleton, K as GoogleIcon, Kt as SettingsCardClassNames, L as DeleteAccountCardProps, Ln as authViewPaths, Lt as RedirectToSignIn, M as CreateOrganizationDialog, Mn as VKIcon, Mt as ProvidersCard, N as CreateOrganizationDialogProps, Nn as XIcon, Nt as ProvidersCardProps, O as AuthViewProps, On as UserInvitationsCard, Ot as PasskeysCard, P as CreateTeamDialog, Pn as ZoomIcon, Pt as RecoverAccountForm, Q as MagicLinkForm, Qt as SignUpForm, R as DeleteOrganizationCard, Rn as getViewByPath, Rt as RedirectToSignUp, S as AuthUIProvider, Sn as UserAvatar, St as OrganizationViewClassNames, T as AuthViewClassNames, Tn as UserButton, Tt as OrganizationViewPaths, U as ForgotPasswordFormProps, Un as useCurrentOrganization, Ut as SessionsCard, V as FacebookIcon, Vn as useAuthData, Vt as RobloxIcon, W as GitHubIcon, Wn as useTheme, Wt as SessionsCardProps, X as LinearIcon, Xt as SignInFormProps, Y as KickIcon, Yt as SignInForm, Z as LinkedInIcon, Zt as SignOut, _ as AuthLoading, _n as UpdateAvatarCardProps, _t as OrganizationSlugCardProps, a as AccountViewPath, an as TeamCell, at as OrganizationInvitationsCard, b as AuthUIContext, bn as UpdateNameCard, bt as OrganizationSwitcherProps, c as AccountsCard, cn as TeamOptionsContext, ct as OrganizationLogoCardProps, d as AppleIcon, dn as TwitchIcon, dt as OrganizationMembersCard, en as SignedIn, et as MicrosoftIcon, f as AuthCallback, fn as TwoFactorCard, ft as OrganizationNameCard, g as AuthHooks, gn as UpdateAvatarCard, gt as OrganizationSlugCard, h as AuthFormProps, hn as TwoFactorFormProps, ht as OrganizationSettingsCardsProps, i as AccountView, in as Team, it as OrganizationCellView, j as ChangePasswordCardProps, jn as UserViewProps, jt as Provider, k as ChangeEmailCard, kn as UserView, kt as PasskeysCardProps, l as AccountsCardProps, ln as TeamsCard, lt as OrganizationLogoClassNames, m as AuthFormClassNames, mn as TwoFactorForm, mt as OrganizationSettingsCards, n as AcceptInvitationCardProps, nn as SlackIcon, nt as NeonAuthUIProviderProps, o as AccountViewPaths, on as TeamCellProps, ot as OrganizationLogo, p as AuthForm, pn as TwoFactorCardProps, pt as OrganizationNameCardProps, q as HuggingFaceIcon, qt as SettingsCardProps, r as AccountSettingsCards, rn as SpotifyIcon, rt as NotionIcon, s as AccountViewProps, sn as TeamOptions, st as OrganizationLogoCard, t as AcceptInvitationCard, tn as SignedOut, tt as NeonAuthUIProvider, u as ApiKeysCardProps, un as TikTokIcon, ut as OrganizationLogoProps, v as AuthLocalization, vn as UpdateFieldCard, vt as OrganizationSwitcher, w as AuthView, wn as UserAvatarProps, wt as OrganizationViewPath, x as AuthUIContextType, xn as UpdateUsernameCard, xt as OrganizationView, y as AuthMutators, yn as UpdateFieldCardProps, yt as OrganizationSwitcherClassNames, z as DiscordIcon, zn as organizationViewPaths, zt as ResetPasswordForm } from "../index-DHryUj7e.mjs";
|
|
5
5
|
import { useStore } from "better-auth/react";
|
package/dist/react/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "../adapter-core-
|
|
2
|
-
import { t as BetterAuthReactAdapter } from "../better-auth-react-adapter-
|
|
1
|
+
import "../adapter-core-BFMM3lwe.mjs";
|
|
2
|
+
import { t as BetterAuthReactAdapter } from "../better-auth-react-adapter-DZTZVVnk.mjs";
|
|
3
3
|
import { $ as RobloxIcon, A as MagicLinkForm, At as authViewPaths, B as OrganizationSettingsCards, C as GitLabIcon, Ct as UserInvitationsCard, D as KickIcon, Dt as ZoomIcon, E as InputFieldSkeleton, Et as XIcon, F as OrganizationInvitationsCard, Ft as useAuthenticate, G as PasskeysCard, H as OrganizationSwitcher, I as OrganizationLogo, It as useCurrentOrganization, J as RecoverAccountForm, K as PasswordInput, L as OrganizationLogoCard, Lt as useTheme, M as NeonAuthUIProvider, Mt as organizationViewPaths, N as NotionIcon, Nt as socialProviders, O as LinearIcon, Ot as accountViewPaths, P as OrganizationCellView, Pt as useAuthData, Q as ResetPasswordForm, R as OrganizationMembersCard, S as GitHubIcon, St as UserButton, T as HuggingFaceIcon, Tt as VKIcon, U as OrganizationView, V as OrganizationSlugCard, W as OrganizationsCard, X as RedirectToSignIn, Y as RedditIcon, Z as RedirectToSignUp, _ as DeleteOrganizationCard, _t as UpdateAvatarCard, a as AppleIcon, at as SignOut, b as FacebookIcon, bt as UpdateUsernameCard, c as AuthLoading, ct as SignedOut, d as AuthView, dt as TeamCell, et as SecuritySettingsCards, f as ChangeEmailCard, ft as TeamsCard, g as DeleteAccountCard, gt as TwoFactorForm, h as CreateTeamDialog, ht as TwoFactorCard, i as AccountsCard, it as SignInForm, j as MicrosoftIcon, jt as getViewByPath, k as LinkedInIcon, kt as authLocalization, l as AuthUIContext, lt as SlackIcon, m as CreateOrganizationDialog, mt as TwitchIcon, n as AccountSettingsCards, nt as SettingsCard, o as AuthCallback, ot as SignUpForm, p as ChangePasswordCard, pt as TikTokIcon, q as ProvidersCard, r as AccountView, rt as SettingsCellSkeleton, s as AuthForm, st as SignedIn, t as AcceptInvitationCard, tt as SessionsCard, u as AuthUIProvider, ut as SpotifyIcon, v as DiscordIcon, vt as UpdateFieldCard, w as GoogleIcon, wt as UserView, x as ForgotPasswordForm, xt as UserAvatar, y as DropboxIcon, yt as UpdateNameCard, z as OrganizationNameCard } from "../ui-CnVnqGns.mjs";
|
|
4
4
|
import { useStore } from "better-auth/react";
|
|
5
5
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as CURRENT_TAB_CLIENT_ID, n as BETTER_AUTH_METHODS_CACHE, r as BETTER_AUTH_METHODS_HOOKS, t as NeonAuthAdapterCore } from "./adapter-core-
|
|
1
|
+
import { i as CURRENT_TAB_CLIENT_ID, n as BETTER_AUTH_METHODS_CACHE, r as BETTER_AUTH_METHODS_HOOKS, t as NeonAuthAdapterCore } from "./adapter-core-BFMM3lwe.mjs";
|
|
2
2
|
import { a as createAuthError, i as AuthErrorCode, l as isAuthError, n as mapBetterAuthSession, r as normalizeBetterAuthError, t as mapBetterAuthIdentity } from "./better-auth-helpers-Bkezghej.mjs";
|
|
3
3
|
import { createAuthClient, getGlobalBroadcastChannel } from "better-auth/client";
|
|
4
4
|
import "@supabase/auth-js";
|