better-auth 1.7.0-beta.1 → 1.7.0-beta.2
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/api/to-auth-endpoints.mjs +57 -11
- package/dist/client/config.mjs +1 -1
- package/dist/context/helpers.mjs +2 -1
- package/dist/cookies/cookie-utils.d.mts +13 -1
- package/dist/cookies/cookie-utils.mjs +16 -1
- package/dist/cookies/index.d.mts +2 -2
- package/dist/cookies/index.mjs +2 -2
- package/dist/integrations/next-js.mjs +2 -10
- package/dist/integrations/svelte-kit.mjs +5 -10
- package/dist/integrations/tanstack-start-solid.mjs +2 -10
- package/dist/integrations/tanstack-start.mjs +2 -10
- package/dist/oauth2/state.mjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugins/custom-session/index.d.mts +6 -5
- package/dist/plugins/custom-session/index.mjs +3 -15
- package/dist/plugins/organization/organization.d.mts +6 -6
- package/dist/plugins/organization/routes/crud-team.d.mts +4 -0
- package/dist/plugins/organization/routes/crud-team.mjs +39 -2
- package/dist/plugins/phone-number/index.d.mts +30 -0
- package/dist/plugins/phone-number/index.mjs +9 -1
- package/dist/plugins/phone-number/routes.mjs +5 -0
- package/dist/plugins/test-utils/index.d.mts +11 -2
- package/dist/plugins/test-utils/index.mjs +11 -2
- package/dist/plugins/two-factor/index.mjs +1 -2
- package/dist/utils/is-api-error.d.mts +1 -5
- package/dist/utils/is-api-error.mjs +1 -7
- package/dist/utils/url.mjs +16 -5
- package/package.json +10 -10
|
@@ -98,16 +98,43 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
98
98
|
[ATTR_HTTP_ROUTE]: route,
|
|
99
99
|
[ATTR_OPERATION_ID]: operationId
|
|
100
100
|
}, () => endpoint(internalContext))).catch((e) => {
|
|
101
|
-
if (isAPIError(e))
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
101
|
+
if (isAPIError(e)) {
|
|
102
|
+
/**
|
|
103
|
+
* API Errors from response are caught
|
|
104
|
+
* and returned to hooks.
|
|
105
|
+
*
|
|
106
|
+
* Headers come from two sources that must both
|
|
107
|
+
* survive:
|
|
108
|
+
* - `kAPIErrorHeaderSymbol`: ctx.responseHeaders
|
|
109
|
+
* accumulated via c.setCookie / c.setHeader
|
|
110
|
+
* before the throw.
|
|
111
|
+
* - `e.headers`: explicit headers on the APIError
|
|
112
|
+
* (e.g. `location` from c.redirect).
|
|
113
|
+
*
|
|
114
|
+
* Start from the accumulated ctx headers, then
|
|
115
|
+
* apply e.headers on top — appending `set-cookie`
|
|
116
|
+
* and setting others — so explicit APIError
|
|
117
|
+
* headers override while cookies accumulate.
|
|
118
|
+
*/
|
|
119
|
+
const ctxHeaders = e[kAPIErrorHeaderSymbol];
|
|
120
|
+
const errHeaders = e.headers ? new Headers(e.headers) : null;
|
|
121
|
+
let headers = null;
|
|
122
|
+
if (ctxHeaders || errHeaders) {
|
|
123
|
+
headers = new Headers();
|
|
124
|
+
ctxHeaders?.forEach((value, key) => {
|
|
125
|
+
headers.append(key, value);
|
|
126
|
+
});
|
|
127
|
+
errHeaders?.forEach((value, key) => {
|
|
128
|
+
if (key.toLowerCase() === "set-cookie") headers.append(key, value);
|
|
129
|
+
else headers.set(key, value);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
response: e,
|
|
134
|
+
status: e.statusCode,
|
|
135
|
+
headers
|
|
136
|
+
};
|
|
137
|
+
}
|
|
111
138
|
throw e;
|
|
112
139
|
});
|
|
113
140
|
if (result && result instanceof Response) return result;
|
|
@@ -116,7 +143,26 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
116
143
|
const after = await runAfterHooks(internalContext, afterHooks, endpoint, operationId);
|
|
117
144
|
if (after.response) result.response = after.response;
|
|
118
145
|
if (isAPIError(result.response) && shouldPublishLog(authContext.logger.level, "debug")) result.response.stack = result.response.errorStack;
|
|
119
|
-
if (isAPIError(result.response) && !shouldReturnResponse)
|
|
146
|
+
if (isAPIError(result.response) && !shouldReturnResponse) {
|
|
147
|
+
/**
|
|
148
|
+
* Non-response path: we re-throw the raw APIError
|
|
149
|
+
* to callers of `auth.api.*`. `result.headers`
|
|
150
|
+
* holds the merged ctx + explicit headers (see
|
|
151
|
+
* catch block above) — rewrite
|
|
152
|
+
* `kAPIErrorHeaderSymbol` with the merged set so
|
|
153
|
+
* downstream pipelines (e.g. better-call's
|
|
154
|
+
* response builder, or an outer hook catch) see
|
|
155
|
+
* the same headers we'd have written on the
|
|
156
|
+
* response.
|
|
157
|
+
*/
|
|
158
|
+
if (result.headers) Object.defineProperty(result.response, kAPIErrorHeaderSymbol, {
|
|
159
|
+
enumerable: false,
|
|
160
|
+
configurable: true,
|
|
161
|
+
writable: false,
|
|
162
|
+
value: result.headers
|
|
163
|
+
});
|
|
164
|
+
throw result.response;
|
|
165
|
+
}
|
|
120
166
|
return shouldReturnResponse ? toResponse(result.response, {
|
|
121
167
|
headers: result.headers,
|
|
122
168
|
status: result.status
|
package/dist/client/config.mjs
CHANGED
|
@@ -67,7 +67,7 @@ const getClientConfig = (options, loadEnv) => {
|
|
|
67
67
|
const atomListeners = [{
|
|
68
68
|
signal: "$sessionSignal",
|
|
69
69
|
matcher(path) {
|
|
70
|
-
return path === "/sign-out" || path === "/update-user" || path === "/update-session" || path === "/sign-up/email" || path === "/sign-in/email" || path === "/delete-user" || path === "/verify-email" || path === "/revoke-sessions" || path === "/revoke-session" || path === "/change-email";
|
|
70
|
+
return path === "/sign-out" || path === "/update-user" || path === "/update-session" || path === "/sign-up/email" || path === "/sign-in/email" || path === "/delete-user" || path === "/verify-email" || path === "/revoke-sessions" || path === "/revoke-session" || path === "/revoke-other-sessions" || path === "/change-email" || path === "/change-password";
|
|
71
71
|
},
|
|
72
72
|
callback(path) {
|
|
73
73
|
if (path === "/sign-out") broadcastSessionUpdate("signout");
|
package/dist/context/helpers.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { createInternalAdapter } from "../db/internal-adapter.mjs";
|
|
|
5
5
|
import { env } from "@better-auth/core/env";
|
|
6
6
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
7
7
|
import { defu } from "defu";
|
|
8
|
+
import { isLoopbackHost } from "@better-auth/core/utils/host";
|
|
8
9
|
//#region src/context/helpers.ts
|
|
9
10
|
async function runPluginInit(context) {
|
|
10
11
|
let options = context.options;
|
|
@@ -62,7 +63,7 @@ async function getTrustedOrigins(options, request) {
|
|
|
62
63
|
const allowedHosts = options.baseURL.allowedHosts;
|
|
63
64
|
for (const host of allowedHosts) if (!host.includes("://")) {
|
|
64
65
|
trustedOrigins.push(`https://${host}`);
|
|
65
|
-
if (
|
|
66
|
+
if (isLoopbackHost(host)) trustedOrigins.push(`http://${host}`);
|
|
66
67
|
} else trustedOrigins.push(host);
|
|
67
68
|
if (options.baseURL.fallback) try {
|
|
68
69
|
trustedOrigins.push(new URL(options.baseURL.fallback).origin);
|
|
@@ -7,9 +7,20 @@ interface CookieAttributes {
|
|
|
7
7
|
path?: string | undefined;
|
|
8
8
|
secure?: boolean | undefined;
|
|
9
9
|
httponly?: boolean | undefined;
|
|
10
|
+
partitioned?: boolean | undefined;
|
|
10
11
|
samesite?: ("strict" | "lax" | "none") | undefined;
|
|
11
12
|
[key: string]: any;
|
|
12
13
|
}
|
|
14
|
+
interface ParsedCookieOptions {
|
|
15
|
+
maxAge?: number | undefined;
|
|
16
|
+
expires?: Date | undefined;
|
|
17
|
+
domain?: string | undefined;
|
|
18
|
+
path?: string | undefined;
|
|
19
|
+
secure?: boolean | undefined;
|
|
20
|
+
httpOnly?: boolean | undefined;
|
|
21
|
+
partitioned?: boolean | undefined;
|
|
22
|
+
sameSite?: CookieAttributes["samesite"];
|
|
23
|
+
}
|
|
13
24
|
declare const SECURE_COOKIE_PREFIX = "__Secure-";
|
|
14
25
|
declare const HOST_COOKIE_PREFIX = "__Host-";
|
|
15
26
|
/**
|
|
@@ -21,8 +32,9 @@ declare function stripSecureCookiePrefix(cookieName: string): string;
|
|
|
21
32
|
*/
|
|
22
33
|
declare function splitSetCookieHeader(setCookie: string): string[];
|
|
23
34
|
declare function parseSetCookieHeader(setCookie: string): Map<string, CookieAttributes>;
|
|
35
|
+
declare function toCookieOptions(attributes: CookieAttributes): ParsedCookieOptions;
|
|
24
36
|
declare function setCookieToHeader(headers: Headers): (context: {
|
|
25
37
|
response: Response;
|
|
26
38
|
}) => void;
|
|
27
39
|
//#endregion
|
|
28
|
-
export { CookieAttributes, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix };
|
|
40
|
+
export { CookieAttributes, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions };
|
|
@@ -78,6 +78,9 @@ function parseSetCookieHeader(setCookie) {
|
|
|
78
78
|
case "samesite":
|
|
79
79
|
attrObj.samesite = attrValue ? attrValue.trim().toLowerCase() : void 0;
|
|
80
80
|
break;
|
|
81
|
+
case "partitioned":
|
|
82
|
+
attrObj.partitioned = true;
|
|
83
|
+
break;
|
|
81
84
|
default:
|
|
82
85
|
attrObj[normalizedAttrName] = attrValue ? attrValue.trim() : true;
|
|
83
86
|
break;
|
|
@@ -87,6 +90,18 @@ function parseSetCookieHeader(setCookie) {
|
|
|
87
90
|
});
|
|
88
91
|
return cookies;
|
|
89
92
|
}
|
|
93
|
+
function toCookieOptions(attributes) {
|
|
94
|
+
return {
|
|
95
|
+
maxAge: attributes["max-age"],
|
|
96
|
+
expires: attributes.expires,
|
|
97
|
+
domain: attributes.domain,
|
|
98
|
+
path: attributes.path,
|
|
99
|
+
secure: attributes.secure,
|
|
100
|
+
httpOnly: attributes.httponly,
|
|
101
|
+
sameSite: attributes.samesite,
|
|
102
|
+
partitioned: attributes.partitioned
|
|
103
|
+
};
|
|
104
|
+
}
|
|
90
105
|
function setCookieToHeader(headers) {
|
|
91
106
|
return (context) => {
|
|
92
107
|
const setCookieHeader = context.response.headers.get("set-cookie");
|
|
@@ -104,4 +119,4 @@ function setCookieToHeader(headers) {
|
|
|
104
119
|
};
|
|
105
120
|
}
|
|
106
121
|
//#endregion
|
|
107
|
-
export { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix };
|
|
122
|
+
export { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions };
|
package/dist/cookies/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Session, User } from "../types/models.mjs";
|
|
2
|
-
import { CookieAttributes, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix } from "./cookie-utils.mjs";
|
|
2
|
+
import { CookieAttributes, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions } from "./cookie-utils.mjs";
|
|
3
3
|
import { createSessionStore, getAccountCookie, getChunkedCookie } from "./session-store.mjs";
|
|
4
4
|
import { BetterAuthCookie, BetterAuthCookies, BetterAuthOptions, GenericEndpointContext } from "@better-auth/core";
|
|
5
5
|
import * as better_call0 from "better-call";
|
|
@@ -116,4 +116,4 @@ declare const getCookieCache: <S extends {
|
|
|
116
116
|
version?: string | ((session: Session & Record<string, any>, user: User & Record<string, any>) => string) | ((session: Session & Record<string, any>, user: User & Record<string, any>) => Promise<string>);
|
|
117
117
|
} | undefined) => Promise<S | null>;
|
|
118
118
|
//#endregion
|
|
119
|
-
export { CookieAttributes, EligibleCookies, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, createCookieGetter, createSessionStore, deleteSessionCookie, expireCookie, getAccountCookie, getChunkedCookie, getCookieCache, getCookies, getSessionCookie, parseCookies, parseSetCookieHeader, setCookieCache, setCookieToHeader, setSessionCookie, splitSetCookieHeader, stripSecureCookiePrefix };
|
|
119
|
+
export { CookieAttributes, EligibleCookies, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, createCookieGetter, createSessionStore, deleteSessionCookie, expireCookie, getAccountCookie, getChunkedCookie, getCookieCache, getCookies, getSessionCookie, parseCookies, parseSetCookieHeader, setCookieCache, setCookieToHeader, setSessionCookie, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions };
|
package/dist/cookies/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { parseUserOutput } from "../db/schema.mjs";
|
|
|
4
4
|
import { getDate } from "../utils/date.mjs";
|
|
5
5
|
import { isPromise } from "../utils/is-promise.mjs";
|
|
6
6
|
import { sec } from "../utils/time.mjs";
|
|
7
|
-
import { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix } from "./cookie-utils.mjs";
|
|
7
|
+
import { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions } from "./cookie-utils.mjs";
|
|
8
8
|
import { createAccountStore, createSessionStore, getAccountCookie, getChunkedCookie, setAccountCookie } from "./session-store.mjs";
|
|
9
9
|
import { env, isProduction } from "@better-auth/core/env";
|
|
10
10
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
@@ -258,4 +258,4 @@ const getCookieCache = async (request, config) => {
|
|
|
258
258
|
return null;
|
|
259
259
|
};
|
|
260
260
|
//#endregion
|
|
261
|
-
export { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, createCookieGetter, createSessionStore, deleteSessionCookie, expireCookie, getAccountCookie, getChunkedCookie, getCookieCache, getCookies, getSessionCookie, parseCookies, parseSetCookieHeader, setCookieCache, setCookieToHeader, setSessionCookie, splitSetCookieHeader, stripSecureCookiePrefix };
|
|
261
|
+
export { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, createCookieGetter, createSessionStore, deleteSessionCookie, expireCookie, getAccountCookie, getChunkedCookie, getCookieCache, getCookies, getSessionCookie, parseCookies, parseSetCookieHeader, setCookieCache, setCookieToHeader, setSessionCookie, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseSetCookieHeader } from "../cookies/cookie-utils.mjs";
|
|
1
|
+
import { parseSetCookieHeader, toCookieOptions } from "../cookies/cookie-utils.mjs";
|
|
2
2
|
import { setShouldSkipSessionRefresh } from "../api/state/should-session-refresh.mjs";
|
|
3
3
|
import { PACKAGE_VERSION } from "../version.mjs";
|
|
4
4
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
@@ -70,16 +70,8 @@ const nextCookies = () => {
|
|
|
70
70
|
}
|
|
71
71
|
parsed.forEach((value, key) => {
|
|
72
72
|
if (!key) return;
|
|
73
|
-
const opts = {
|
|
74
|
-
sameSite: value.samesite,
|
|
75
|
-
secure: value.secure,
|
|
76
|
-
maxAge: value["max-age"],
|
|
77
|
-
httpOnly: value.httponly,
|
|
78
|
-
domain: value.domain,
|
|
79
|
-
path: value.path
|
|
80
|
-
};
|
|
81
73
|
try {
|
|
82
|
-
cookieHelper.set(key, value.value,
|
|
74
|
+
cookieHelper.set(key, value.value, toCookieOptions(value));
|
|
83
75
|
} catch {}
|
|
84
76
|
});
|
|
85
77
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseSetCookieHeader } from "../cookies/cookie-utils.mjs";
|
|
1
|
+
import { parseSetCookieHeader, toCookieOptions } from "../cookies/cookie-utils.mjs";
|
|
2
2
|
import { PACKAGE_VERSION } from "../version.mjs";
|
|
3
3
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
4
4
|
//#region src/integrations/svelte-kit.ts
|
|
@@ -36,15 +36,10 @@ const sveltekitCookies = (getRequestEvent) => {
|
|
|
36
36
|
const event = getRequestEvent();
|
|
37
37
|
if (!event) return;
|
|
38
38
|
const parsed = parseSetCookieHeader(setCookies);
|
|
39
|
-
for (const [name,
|
|
40
|
-
event.cookies.set(name, value, {
|
|
41
|
-
|
|
42
|
-
path:
|
|
43
|
-
expires: ops.expires,
|
|
44
|
-
secure: ops.secure,
|
|
45
|
-
httpOnly: ops.httponly,
|
|
46
|
-
domain: ops.domain,
|
|
47
|
-
maxAge: ops["max-age"]
|
|
39
|
+
for (const [name, attributes] of parsed) try {
|
|
40
|
+
event.cookies.set(name, attributes.value, {
|
|
41
|
+
...toCookieOptions(attributes),
|
|
42
|
+
path: attributes.path || "/"
|
|
48
43
|
});
|
|
49
44
|
} catch {}
|
|
50
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseSetCookieHeader } from "../cookies/cookie-utils.mjs";
|
|
1
|
+
import { parseSetCookieHeader, toCookieOptions } from "../cookies/cookie-utils.mjs";
|
|
2
2
|
import { PACKAGE_VERSION } from "../version.mjs";
|
|
3
3
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
4
4
|
//#region src/integrations/tanstack-start-solid.ts
|
|
@@ -37,16 +37,8 @@ const tanstackStartCookies = () => {
|
|
|
37
37
|
const { setCookie } = await import("@tanstack/solid-start/server");
|
|
38
38
|
parsed.forEach((value, key) => {
|
|
39
39
|
if (!key) return;
|
|
40
|
-
const opts = {
|
|
41
|
-
sameSite: value.samesite,
|
|
42
|
-
secure: value.secure,
|
|
43
|
-
maxAge: value["max-age"],
|
|
44
|
-
httpOnly: value.httponly,
|
|
45
|
-
domain: value.domain,
|
|
46
|
-
path: value.path
|
|
47
|
-
};
|
|
48
40
|
try {
|
|
49
|
-
setCookie(key, value.value,
|
|
41
|
+
setCookie(key, value.value, toCookieOptions(value));
|
|
50
42
|
} catch {}
|
|
51
43
|
});
|
|
52
44
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseSetCookieHeader } from "../cookies/cookie-utils.mjs";
|
|
1
|
+
import { parseSetCookieHeader, toCookieOptions } from "../cookies/cookie-utils.mjs";
|
|
2
2
|
import { PACKAGE_VERSION } from "../version.mjs";
|
|
3
3
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
4
4
|
//#region src/integrations/tanstack-start.ts
|
|
@@ -37,16 +37,8 @@ const tanstackStartCookies = () => {
|
|
|
37
37
|
const { setCookie } = await import("@tanstack/react-start/server");
|
|
38
38
|
parsed.forEach((value, key) => {
|
|
39
39
|
if (!key) return;
|
|
40
|
-
const opts = {
|
|
41
|
-
sameSite: value.samesite,
|
|
42
|
-
secure: value.secure,
|
|
43
|
-
maxAge: value["max-age"],
|
|
44
|
-
httpOnly: value.httponly,
|
|
45
|
-
domain: value.domain,
|
|
46
|
-
path: value.path
|
|
47
|
-
};
|
|
48
40
|
try {
|
|
49
|
-
setCookie(key, value.value,
|
|
41
|
+
setCookie(key, value.value, toCookieOptions(value));
|
|
50
42
|
} catch {}
|
|
51
43
|
});
|
|
52
44
|
return;
|
package/dist/oauth2/state.mjs
CHANGED
|
@@ -29,7 +29,7 @@ async function generateState(c, link, additionalData) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
async function parseState(c) {
|
|
32
|
-
const state = c.query.state || c.body
|
|
32
|
+
const state = c.query.state || c.body?.state;
|
|
33
33
|
const errorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`;
|
|
34
34
|
let parsedData;
|
|
35
35
|
try {
|
package/dist/package.mjs
CHANGED
|
@@ -2,7 +2,8 @@ import * as _better_auth_core0 from "@better-auth/core";
|
|
|
2
2
|
import { BetterAuthOptions, GenericEndpointContext } from "@better-auth/core";
|
|
3
3
|
import { Session, User } from "@better-auth/core/db";
|
|
4
4
|
import * as better_call0 from "better-call";
|
|
5
|
-
import * as
|
|
5
|
+
import * as zod from "zod";
|
|
6
|
+
import * as zod_v4_core0 from "zod/v4/core";
|
|
6
7
|
|
|
7
8
|
//#region src/plugins/custom-session/index.d.ts
|
|
8
9
|
declare module "@better-auth/core" {
|
|
@@ -34,10 +35,10 @@ declare const customSession: <Returns extends Record<string, any>, O extends Bet
|
|
|
34
35
|
endpoints: {
|
|
35
36
|
getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
36
37
|
method: "GET";
|
|
37
|
-
query:
|
|
38
|
-
disableCookieCache:
|
|
39
|
-
disableRefresh:
|
|
40
|
-
},
|
|
38
|
+
query: zod.ZodOptional<zod.ZodObject<{
|
|
39
|
+
disableCookieCache: zod.ZodOptional<zod.ZodCoercedBoolean<unknown>>;
|
|
40
|
+
disableRefresh: zod.ZodOptional<zod.ZodCoercedBoolean<unknown>>;
|
|
41
|
+
}, zod_v4_core0.$strip>>;
|
|
41
42
|
metadata: {
|
|
42
43
|
CUSTOM_SESSION: boolean;
|
|
43
44
|
openapi: {
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import { parseSetCookieHeader } from "../../cookies/cookie-utils.mjs";
|
|
1
|
+
import { parseSetCookieHeader, toCookieOptions } from "../../cookies/cookie-utils.mjs";
|
|
2
|
+
import { getSessionQuerySchema } from "../../cookies/session-store.mjs";
|
|
2
3
|
import { getSession } from "../../api/routes/session.mjs";
|
|
3
4
|
import { PACKAGE_VERSION } from "../../version.mjs";
|
|
4
5
|
import { getEndpointResponse } from "../../utils/plugin-helper.mjs";
|
|
5
6
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
6
|
-
import * as z from "zod";
|
|
7
7
|
//#region src/plugins/custom-session/index.ts
|
|
8
|
-
const getSessionQuerySchema = z.optional(z.object({
|
|
9
|
-
disableCookieCache: z.boolean().meta({ description: "Disable cookie cache and fetch session from database" }).or(z.string().transform((v) => v === "true")).optional(),
|
|
10
|
-
disableRefresh: z.boolean().meta({ description: "Disable session refresh. Useful for checking session status, without updating the session" }).optional()
|
|
11
|
-
}));
|
|
12
8
|
const customSession = (fn, options, pluginOptions) => {
|
|
13
9
|
return {
|
|
14
10
|
id: "custom-session",
|
|
@@ -53,15 +49,7 @@ const customSession = (fn, options, pluginOptions) => {
|
|
|
53
49
|
if (!session?.response) return ctx.json(null);
|
|
54
50
|
const fnResult = await fn(session.response, ctx);
|
|
55
51
|
for (const cookieStr of session.headers.getSetCookie()) parseSetCookieHeader(cookieStr).forEach((attrs, name) => {
|
|
56
|
-
ctx.setCookie(name, attrs.value,
|
|
57
|
-
maxAge: attrs["max-age"],
|
|
58
|
-
expires: attrs.expires,
|
|
59
|
-
domain: attrs.domain,
|
|
60
|
-
path: attrs.path,
|
|
61
|
-
secure: attrs.secure,
|
|
62
|
-
httpOnly: attrs.httponly,
|
|
63
|
-
sameSite: attrs.samesite
|
|
64
|
-
});
|
|
52
|
+
ctx.setCookie(name, attrs.value, toCookieOptions(attrs));
|
|
65
53
|
});
|
|
66
54
|
session.headers.delete("set-cookie");
|
|
67
55
|
session.headers.forEach((value, key) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AccessControl, ArrayElement, Statements } from "../access/types.mjs";
|
|
2
2
|
import { OrganizationOptions } from "./types.mjs";
|
|
3
|
-
import { InferInvitation, InferMember, InferOrganization, InferTeam, OrganizationSchema,
|
|
3
|
+
import { InferInvitation, InferMember, InferOrganization, InferTeam, OrganizationSchema, TeamMember } from "./schema.mjs";
|
|
4
4
|
import { ORGANIZATION_ERROR_CODES } from "./error-codes.mjs";
|
|
5
5
|
import { createOrgRole, deleteOrgRole, getOrgRole, listOrgRoles, updateOrgRole } from "./routes/crud-access-control.mjs";
|
|
6
6
|
import { acceptInvitation, cancelInvitation, createInvitation, getInvitation, listInvitations, listUserInvitations, rejectInvitation } from "./routes/crud-invites.mjs";
|
|
@@ -30,7 +30,7 @@ type DefaultOrganizationPlugin<Options extends OrganizationOptions> = {
|
|
|
30
30
|
Member: InferMember<Options>;
|
|
31
31
|
Team: Options["teams"] extends {
|
|
32
32
|
enabled: true;
|
|
33
|
-
} ?
|
|
33
|
+
} ? InferTeam<Options> : never;
|
|
34
34
|
TeamMember: Options["teams"] extends {
|
|
35
35
|
enabled: true;
|
|
36
36
|
} ? TeamMember : never;
|
|
@@ -249,7 +249,7 @@ type OrganizationPlugin<O extends OrganizationOptions> = {
|
|
|
249
249
|
Member: InferMember<O>;
|
|
250
250
|
Team: O["teams"] extends {
|
|
251
251
|
enabled: true;
|
|
252
|
-
} ?
|
|
252
|
+
} ? InferTeam<O> : never;
|
|
253
253
|
TeamMember: O["teams"] extends {
|
|
254
254
|
enabled: true;
|
|
255
255
|
} ? TeamMember : never;
|
|
@@ -300,7 +300,7 @@ declare function organization<O extends OrganizationOptions & {
|
|
|
300
300
|
Member: InferMember<O>;
|
|
301
301
|
Team: O["teams"] extends {
|
|
302
302
|
enabled: true;
|
|
303
|
-
} ?
|
|
303
|
+
} ? InferTeam<O> : never;
|
|
304
304
|
TeamMember: O["teams"] extends {
|
|
305
305
|
enabled: true;
|
|
306
306
|
} ? TeamMember : never;
|
|
@@ -336,7 +336,7 @@ declare function organization<O extends OrganizationOptions & {
|
|
|
336
336
|
Member: InferMember<O>;
|
|
337
337
|
Team: O["teams"] extends {
|
|
338
338
|
enabled: true;
|
|
339
|
-
} ?
|
|
339
|
+
} ? InferTeam<O> : never;
|
|
340
340
|
TeamMember: O["teams"] extends {
|
|
341
341
|
enabled: true;
|
|
342
342
|
} ? TeamMember : never;
|
|
@@ -372,7 +372,7 @@ declare function organization<O extends OrganizationOptions & {
|
|
|
372
372
|
Member: InferMember<O>;
|
|
373
373
|
Team: O["teams"] extends {
|
|
374
374
|
enabled: true;
|
|
375
|
-
} ?
|
|
375
|
+
} ? InferTeam<O> : never;
|
|
376
376
|
TeamMember: O["teams"] extends {
|
|
377
377
|
enabled: true;
|
|
378
378
|
} ? TeamMember : never;
|
|
@@ -576,6 +576,10 @@ declare const setActiveTeam: <O extends OrganizationOptions>(options: O) => bett
|
|
|
576
576
|
} | null>;
|
|
577
577
|
declare const listUserTeams: <O extends OrganizationOptions>(options: O) => better_call0.StrictEndpoint<"/organization/list-user-teams", {
|
|
578
578
|
method: "GET";
|
|
579
|
+
query: z.ZodOptional<z.ZodObject<{
|
|
580
|
+
userId: z.ZodOptional<z.ZodString>;
|
|
581
|
+
organizationId: z.ZodOptional<z.ZodString>;
|
|
582
|
+
}, z.core.$strip>>;
|
|
579
583
|
metadata: {
|
|
580
584
|
openapi: {
|
|
581
585
|
description: string;
|
|
@@ -417,8 +417,12 @@ const setActiveTeam = (options) => createAuthEndpoint("/organization/set-active-
|
|
|
417
417
|
});
|
|
418
418
|
const listUserTeams = (options) => createAuthEndpoint("/organization/list-user-teams", {
|
|
419
419
|
method: "GET",
|
|
420
|
+
query: z.object({
|
|
421
|
+
userId: z.string().optional().meta({ description: "The user ID to list teams for. Defaults to the current session user." }),
|
|
422
|
+
organizationId: z.string().optional().meta({ description: "The organization ID to scope the team list to. When omitted on a self-query, teams are returned across every organization the user belongs to. When querying another user, falls back to the session's active organization and is required if there is no active organization." })
|
|
423
|
+
}).optional(),
|
|
420
424
|
metadata: { openapi: {
|
|
421
|
-
description: "List
|
|
425
|
+
description: "List teams for a user. Without parameters, returns teams for the current user across every organization they belong to. Pass `organizationId` to scope the result to a specific organization. Pass `userId` to list teams for another member; this requires `member:update` permission in the target organization (the explicit `organizationId` if provided, otherwise the session's active organization).",
|
|
422
426
|
responses: { "200": {
|
|
423
427
|
description: "Teams retrieved successfully",
|
|
424
428
|
content: { "application/json": { schema: {
|
|
@@ -436,7 +440,40 @@ const listUserTeams = (options) => createAuthEndpoint("/organization/list-user-t
|
|
|
436
440
|
use: [orgMiddleware, orgSessionMiddleware]
|
|
437
441
|
}, async (ctx) => {
|
|
438
442
|
const session = ctx.context.session;
|
|
439
|
-
const
|
|
443
|
+
const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
|
|
444
|
+
const targetUserId = ctx.query?.userId || session.user.id;
|
|
445
|
+
const isSelf = targetUserId === session.user.id;
|
|
446
|
+
const organizationId = ctx.query?.organizationId || session.session.activeOrganizationId;
|
|
447
|
+
const isExplicitOrg = Boolean(ctx.query?.organizationId);
|
|
448
|
+
if (!isSelf) {
|
|
449
|
+
if (!organizationId) throw APIError.from("BAD_REQUEST", ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION);
|
|
450
|
+
const requesterMember = await adapter.findMemberByOrgId({
|
|
451
|
+
userId: session.user.id,
|
|
452
|
+
organizationId
|
|
453
|
+
});
|
|
454
|
+
if (!requesterMember) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION);
|
|
455
|
+
if (!await hasPermission({
|
|
456
|
+
role: requesterMember.role,
|
|
457
|
+
options: ctx.context.orgOptions,
|
|
458
|
+
permissions: { member: ["update"] },
|
|
459
|
+
organizationId
|
|
460
|
+
}, ctx)) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER);
|
|
461
|
+
if (!await adapter.findMemberByOrgId({
|
|
462
|
+
userId: targetUserId,
|
|
463
|
+
organizationId
|
|
464
|
+
})) throw APIError.from("BAD_REQUEST", ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION);
|
|
465
|
+
const teams = await adapter.listTeamsByUser({ userId: targetUserId });
|
|
466
|
+
return ctx.json(teams.filter((t) => t.organizationId === organizationId));
|
|
467
|
+
}
|
|
468
|
+
if (isExplicitOrg && organizationId) {
|
|
469
|
+
if (!await adapter.findMemberByOrgId({
|
|
470
|
+
userId: session.user.id,
|
|
471
|
+
organizationId
|
|
472
|
+
})) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION);
|
|
473
|
+
const teams = await adapter.listTeamsByUser({ userId: session.user.id });
|
|
474
|
+
return ctx.json(teams.filter((t) => t.organizationId === organizationId));
|
|
475
|
+
}
|
|
476
|
+
const teams = await adapter.listTeamsByUser({ userId: session.user.id });
|
|
440
477
|
return ctx.json(teams);
|
|
441
478
|
});
|
|
442
479
|
const listTeamMembersQuerySchema = z.optional(z.object({ teamId: z.string().optional().meta({ description: "The team whose members we should return. If this is not provided the members of the current active team get returned." }) }));
|
|
@@ -16,6 +16,36 @@ declare module "@better-auth/core" {
|
|
|
16
16
|
declare const phoneNumber: (options?: PhoneNumberOptions | undefined) => {
|
|
17
17
|
id: "phone-number";
|
|
18
18
|
version: string;
|
|
19
|
+
init(): {
|
|
20
|
+
options: {
|
|
21
|
+
databaseHooks: {
|
|
22
|
+
user: {
|
|
23
|
+
update: {
|
|
24
|
+
before(data: Partial<{
|
|
25
|
+
id: string;
|
|
26
|
+
createdAt: Date;
|
|
27
|
+
updatedAt: Date;
|
|
28
|
+
email: string;
|
|
29
|
+
emailVerified: boolean;
|
|
30
|
+
name: string;
|
|
31
|
+
image?: string | null | undefined;
|
|
32
|
+
}> & Record<string, unknown>): Promise<{
|
|
33
|
+
data: {
|
|
34
|
+
[x: string]: unknown;
|
|
35
|
+
id?: string | undefined;
|
|
36
|
+
createdAt?: Date | undefined;
|
|
37
|
+
updatedAt?: Date | undefined;
|
|
38
|
+
email?: string | undefined;
|
|
39
|
+
emailVerified?: boolean | undefined;
|
|
40
|
+
name?: string | undefined;
|
|
41
|
+
image?: string | null | undefined;
|
|
42
|
+
};
|
|
43
|
+
} | undefined>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
};
|
|
19
49
|
hooks: {
|
|
20
50
|
before: {
|
|
21
51
|
matcher: (ctx: _better_auth_core0.HookEndpointContext) => boolean;
|
|
@@ -19,8 +19,16 @@ const phoneNumber = (options) => {
|
|
|
19
19
|
return {
|
|
20
20
|
id: "phone-number",
|
|
21
21
|
version: PACKAGE_VERSION,
|
|
22
|
+
init() {
|
|
23
|
+
return { options: { databaseHooks: { user: { update: { async before(data) {
|
|
24
|
+
if (opts.phoneNumber in data && data[opts.phoneNumber] === null) return { data: {
|
|
25
|
+
...data,
|
|
26
|
+
[opts.phoneNumberVerified]: false
|
|
27
|
+
} };
|
|
28
|
+
} } } } } };
|
|
29
|
+
},
|
|
22
30
|
hooks: { before: [{
|
|
23
|
-
matcher: (ctx) => ctx.path === "/update-user" && "phoneNumber" in ctx.body,
|
|
31
|
+
matcher: (ctx) => ctx.path === "/update-user" && "phoneNumber" in ctx.body && ctx.body.phoneNumber !== null,
|
|
24
32
|
handler: createAuthMiddleware(async (_ctx) => {
|
|
25
33
|
throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.PHONE_NUMBER_CANNOT_BE_UPDATED);
|
|
26
34
|
})
|
|
@@ -312,6 +312,11 @@ const verifyPhoneNumber = (opts) => createAuthEndpoint("/phone-number/verify", {
|
|
|
312
312
|
[opts.phoneNumber]: ctx.body.phoneNumber,
|
|
313
313
|
[opts.phoneNumberVerified]: true
|
|
314
314
|
});
|
|
315
|
+
if (!user) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_UPDATE_USER);
|
|
316
|
+
await opts?.callbackOnVerification?.({
|
|
317
|
+
phoneNumber: ctx.body.phoneNumber,
|
|
318
|
+
user
|
|
319
|
+
}, ctx);
|
|
315
320
|
return ctx.json({
|
|
316
321
|
status: true,
|
|
317
322
|
token: session.session.token,
|
|
@@ -18,19 +18,28 @@ declare module "@better-auth/core" {
|
|
|
18
18
|
* - Auth helpers (login, getAuthHeaders, getCookies)
|
|
19
19
|
* - OTP capture (when captureOTP: true)
|
|
20
20
|
*
|
|
21
|
+
* This plugin does not register public HTTP routes or API endpoints, but it does
|
|
22
|
+
* expose privileged helpers on `ctx.test` for creating sessions and mutating data.
|
|
23
|
+
* Prefer including it in a test-only auth instance such as `auth.test.ts` instead
|
|
24
|
+
* of a production auth config.
|
|
25
|
+
*
|
|
26
|
+
* If you conditionally spread it into `plugins`, TypeScript may stop inferring
|
|
27
|
+
* `ctx.test` correctly. A separate test-only auth instance keeps the helpers
|
|
28
|
+
* typed without adding the plugin to your production auth config.
|
|
29
|
+
*
|
|
21
30
|
* @example
|
|
22
31
|
* ```ts
|
|
23
32
|
* import { betterAuth } from "better-auth";
|
|
24
33
|
* import { testUtils } from "better-auth/plugins";
|
|
25
34
|
*
|
|
26
|
-
* export const
|
|
35
|
+
* export const testAuth = betterAuth({
|
|
27
36
|
* plugins: [
|
|
28
37
|
* testUtils({ captureOTP: true }),
|
|
29
38
|
* ],
|
|
30
39
|
* });
|
|
31
40
|
*
|
|
32
41
|
* // In tests, access helpers via context:
|
|
33
|
-
* const ctx = await
|
|
42
|
+
* const ctx = await testAuth.$context;
|
|
34
43
|
* const test = ctx.test;
|
|
35
44
|
*
|
|
36
45
|
* const user = test.createUser({ email: "test@example.com" });
|
|
@@ -13,19 +13,28 @@ import { createOTPStore } from "./otp-sink.mjs";
|
|
|
13
13
|
* - Auth helpers (login, getAuthHeaders, getCookies)
|
|
14
14
|
* - OTP capture (when captureOTP: true)
|
|
15
15
|
*
|
|
16
|
+
* This plugin does not register public HTTP routes or API endpoints, but it does
|
|
17
|
+
* expose privileged helpers on `ctx.test` for creating sessions and mutating data.
|
|
18
|
+
* Prefer including it in a test-only auth instance such as `auth.test.ts` instead
|
|
19
|
+
* of a production auth config.
|
|
20
|
+
*
|
|
21
|
+
* If you conditionally spread it into `plugins`, TypeScript may stop inferring
|
|
22
|
+
* `ctx.test` correctly. A separate test-only auth instance keeps the helpers
|
|
23
|
+
* typed without adding the plugin to your production auth config.
|
|
24
|
+
*
|
|
16
25
|
* @example
|
|
17
26
|
* ```ts
|
|
18
27
|
* import { betterAuth } from "better-auth";
|
|
19
28
|
* import { testUtils } from "better-auth/plugins";
|
|
20
29
|
*
|
|
21
|
-
* export const
|
|
30
|
+
* export const testAuth = betterAuth({
|
|
22
31
|
* plugins: [
|
|
23
32
|
* testUtils({ captureOTP: true }),
|
|
24
33
|
* ],
|
|
25
34
|
* });
|
|
26
35
|
*
|
|
27
36
|
* // In tests, access helpers via context:
|
|
28
|
-
* const ctx = await
|
|
37
|
+
* const ctx = await testAuth.$context;
|
|
29
38
|
* const test = ctx.test;
|
|
30
39
|
*
|
|
31
40
|
* const user = test.createUser({ email: "test@example.com" });
|
|
@@ -212,13 +212,12 @@ const twoFactor = (options) => {
|
|
|
212
212
|
options,
|
|
213
213
|
hooks: { after: [{
|
|
214
214
|
matcher(context) {
|
|
215
|
-
return context.context.
|
|
215
|
+
return context.path === "/sign-in/email" || context.path === "/sign-in/username" || context.path === "/sign-in/phone-number";
|
|
216
216
|
},
|
|
217
217
|
handler: createAuthMiddleware(async (ctx) => {
|
|
218
218
|
const data = ctx.context.newSession;
|
|
219
219
|
if (!data) return;
|
|
220
220
|
if (!data?.user.twoFactorEnabled) return;
|
|
221
|
-
if (ctx.context.session) return;
|
|
222
221
|
const trustDeviceCookieAttrs = ctx.context.createAuthCookie(TRUST_DEVICE_COOKIE_NAME, { maxAge: trustDeviceMaxAge });
|
|
223
222
|
const trustDeviceCookie = await ctx.getSignedCookie(trustDeviceCookieAttrs.name, ctx.context.secret);
|
|
224
223
|
if (trustDeviceCookie) {
|
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/utils/is-api-error.d.ts
|
|
4
|
-
declare function isAPIError(error: unknown): error is APIError;
|
|
5
|
-
//#endregion
|
|
1
|
+
import { isAPIError } from "@better-auth/core/utils/is-api-error";
|
|
6
2
|
export { isAPIError };
|
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { APIError as APIError$1 } from "better-call";
|
|
3
|
-
//#region src/utils/is-api-error.ts
|
|
4
|
-
function isAPIError(error) {
|
|
5
|
-
return error instanceof APIError$1 || error instanceof APIError || error?.name === "APIError";
|
|
6
|
-
}
|
|
7
|
-
//#endregion
|
|
1
|
+
import { isAPIError } from "@better-auth/core/utils/is-api-error";
|
|
8
2
|
export { isAPIError };
|
package/dist/utils/url.mjs
CHANGED
|
@@ -2,6 +2,21 @@ import { wildcardMatch } from "./wildcard.mjs";
|
|
|
2
2
|
import { env } from "@better-auth/core/env";
|
|
3
3
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
4
4
|
//#region src/utils/url.ts
|
|
5
|
+
/**
|
|
6
|
+
* Minimal loopback check for dev scheme inference only. Reachable from
|
|
7
|
+
* `client/config.ts` via `getBaseURL`, so we MUST NOT import the full
|
|
8
|
+
* `@better-auth/core/utils/host` classifier here: its `utils/ip` dependency
|
|
9
|
+
* on zod would leak into the client bundle (see `e2e/smoke/test/vite.spec.ts`).
|
|
10
|
+
*
|
|
11
|
+
* Server-side SSRF/loopback checks (oauth redirect matching, trusted-origin
|
|
12
|
+
* resolution, electron fetch gate) continue to use the authoritative
|
|
13
|
+
* `isLoopbackHost` from `@better-auth/core/utils/host`. This helper's only
|
|
14
|
+
* job is picking `http` vs `https` for dev ergonomics.
|
|
15
|
+
*/
|
|
16
|
+
function isLoopbackForDevScheme(host) {
|
|
17
|
+
const hostname = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "").toLowerCase();
|
|
18
|
+
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1" || hostname.startsWith("127.");
|
|
19
|
+
}
|
|
5
20
|
function checkHasPath(url) {
|
|
6
21
|
try {
|
|
7
22
|
return (new URL(url).pathname.replace(/\/+$/, "") || "/") !== "/";
|
|
@@ -146,13 +161,9 @@ function getProtocolFromSource(source, configProtocol, trustedProxyHeaders) {
|
|
|
146
161
|
if (url.protocol === "http:" || url.protocol === "https:") return url.protocol.slice(0, -1);
|
|
147
162
|
} catch {}
|
|
148
163
|
const host = getHostFromSource(source, trustedProxyHeaders);
|
|
149
|
-
if (host &&
|
|
164
|
+
if (host && isLoopbackForDevScheme(host)) return "http";
|
|
150
165
|
return "https";
|
|
151
166
|
}
|
|
152
|
-
function isLoopbackHost(host) {
|
|
153
|
-
const h = host.toLowerCase();
|
|
154
|
-
return h === "localhost" || h.startsWith("localhost:") || h === "127.0.0.1" || h.startsWith("127.0.0.1:") || h === "[::1]" || h.startsWith("[::1]:") || h === "0.0.0.0" || h.startsWith("0.0.0.0:");
|
|
155
|
-
}
|
|
156
167
|
/**
|
|
157
168
|
* Matches a hostname against a host pattern.
|
|
158
169
|
* Supports wildcard patterns like `*.vercel.app` or `preview-*.myapp.com`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth",
|
|
3
|
-
"version": "1.7.0-beta.
|
|
3
|
+
"version": "1.7.0-beta.2",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -489,13 +489,13 @@
|
|
|
489
489
|
"kysely": "^0.28.14",
|
|
490
490
|
"nanostores": "^1.1.1",
|
|
491
491
|
"zod": "^4.3.6",
|
|
492
|
-
"@better-auth/core": "1.7.0-beta.
|
|
493
|
-
"@better-auth/drizzle-adapter": "1.7.0-beta.
|
|
494
|
-
"@better-auth/kysely-adapter": "1.7.0-beta.
|
|
495
|
-
"@better-auth/memory-adapter": "1.7.0-beta.
|
|
496
|
-
"@better-auth/mongo-adapter": "1.7.0-beta.
|
|
497
|
-
"@better-auth/prisma-adapter": "1.7.0-beta.
|
|
498
|
-
"@better-auth/telemetry": "1.7.0-beta.
|
|
492
|
+
"@better-auth/core": "1.7.0-beta.2",
|
|
493
|
+
"@better-auth/drizzle-adapter": "1.7.0-beta.2",
|
|
494
|
+
"@better-auth/kysely-adapter": "1.7.0-beta.2",
|
|
495
|
+
"@better-auth/memory-adapter": "1.7.0-beta.2",
|
|
496
|
+
"@better-auth/mongo-adapter": "1.7.0-beta.2",
|
|
497
|
+
"@better-auth/prisma-adapter": "1.7.0-beta.2",
|
|
498
|
+
"@better-auth/telemetry": "1.7.0-beta.2"
|
|
499
499
|
},
|
|
500
500
|
"devDependencies": {
|
|
501
501
|
"@lynx-js/react": "^0.116.3",
|
|
@@ -512,7 +512,7 @@
|
|
|
512
512
|
"happy-dom": "^20.8.9",
|
|
513
513
|
"listhen": "^1.9.0",
|
|
514
514
|
"msw": "^2.12.10",
|
|
515
|
-
"next": "^16.2.
|
|
515
|
+
"next": "^16.2.3",
|
|
516
516
|
"oauth2-mock-server": "^8.2.2",
|
|
517
517
|
"react": "^19.2.4",
|
|
518
518
|
"react-dom": "^19.2.4",
|
|
@@ -531,7 +531,7 @@
|
|
|
531
531
|
"@tanstack/solid-start": "^1.0.0",
|
|
532
532
|
"better-sqlite3": "^12.0.0",
|
|
533
533
|
"drizzle-kit": ">=0.31.4",
|
|
534
|
-
"drizzle-orm": "
|
|
534
|
+
"drizzle-orm": "^0.45.2",
|
|
535
535
|
"mongodb": "^6.0.0 || ^7.0.0",
|
|
536
536
|
"mysql2": "^3.0.0",
|
|
537
537
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|