@better-auth/infra 0.4.8 → 0.4.9
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/CHANGELOG.md +6 -0
- package/dist/client.mjs +2 -2
- package/dist/{constants-C8_UJzzu.mjs → constants-CdZodAXp.mjs} +1 -1
- package/dist/{crypto-DRS7yhmR.mjs → crypto-DDadesq-.mjs} +1 -1
- package/dist/email.mjs +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +161 -114
- package/dist/native.mjs +2 -2
- package/dist/{pow-retry-Di0eUKbA.mjs → pow-retry-DRUkPcBn.mjs} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to `@better-auth/infra` are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0/).
|
|
7
7
|
|
|
8
|
+
## [0.4.9] - 2026-09-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Auth base URL resolution** — Correctly handle Better Auth `baseURL` with an embedded path and dynamic `allowedHosts` (1.5+): dash config, invitation callbacks, SSO/SCIM endpoints, and Sentinel no longer use the wrong mount path or break on object `baseURL`.
|
|
13
|
+
|
|
8
14
|
## [0.4.8] - 2026-09-09
|
|
9
15
|
|
|
10
16
|
### Added
|
package/dist/client.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as hash, o as createKV } from "./crypto-
|
|
2
|
-
import { a as resolveSentinelClientIdentifyUrl, c as identify, i as solvePoWChallenge, l as dashClient, n as decodePoWChallenge, o as resolveIdentifyClientRetry, r as encodePoWSolution, s as generateRequestId, t as createPowRetryTimeout } from "./pow-retry-
|
|
1
|
+
import { n as hash, o as createKV } from "./crypto-DDadesq-.mjs";
|
|
2
|
+
import { a as resolveSentinelClientIdentifyUrl, c as identify, i as solvePoWChallenge, l as dashClient, n as decodePoWChallenge, o as resolveIdentifyClientRetry, r as encodePoWSolution, s as generateRequestId, t as createPowRetryTimeout } from "./pow-retry-DRUkPcBn.mjs";
|
|
3
3
|
import { env } from "@better-auth/core/env";
|
|
4
4
|
//#region src/sentinel/fingerprint.ts
|
|
5
5
|
function murmurhash3(str, seed = 0) {
|
package/dist/email.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as INFRA_USER_AGENT, n as INFRA_API_URL } from "./constants-
|
|
1
|
+
import { i as INFRA_USER_AGENT, n as INFRA_API_URL } from "./constants-CdZodAXp.mjs";
|
|
2
2
|
import { logger } from "better-auth";
|
|
3
3
|
import { env } from "@better-auth/core/env";
|
|
4
4
|
import { createFetch } from "@better-fetch/fetch";
|
package/dist/index.d.mts
CHANGED
|
@@ -205,7 +205,9 @@ interface DashConfigResponse {
|
|
|
205
205
|
deleteUserEnabled: boolean;
|
|
206
206
|
modelName?: string;
|
|
207
207
|
};
|
|
208
|
-
|
|
208
|
+
/** Origin only (never the dynamic allowedHosts object). */
|
|
209
|
+
baseURL: string;
|
|
210
|
+
/** Mount path; reflects path embedded in baseURL when present. */
|
|
209
211
|
basePath: string;
|
|
210
212
|
emailVerification: {
|
|
211
213
|
sendVerificationEmailEnabled: boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as INFRA_USER_AGENT, n as INFRA_API_URL, o as PLUGIN_VERSION, r as INFRA_KV_URL } from "./constants-
|
|
2
|
-
import { a as createAPI, n as hash$1, o as createKV, r as hmacSha256Hex } from "./crypto-
|
|
1
|
+
import { i as INFRA_USER_AGENT, n as INFRA_API_URL, o as PLUGIN_VERSION, r as INFRA_KV_URL } from "./constants-CdZodAXp.mjs";
|
|
2
|
+
import { a as createAPI, n as hash$1, o as createKV, r as hmacSha256Hex } from "./crypto-DDadesq-.mjs";
|
|
3
3
|
import { EMAIL_TEMPLATES, createEmailSender, sendBulkEmails, sendEmail } from "./email.mjs";
|
|
4
4
|
import { getCurrentAdapter, getCurrentAuthContext, getCurrentDBAdapterAsyncLocalStorage, runWithTransaction } from "@better-auth/core/context";
|
|
5
5
|
import { APIError, generateId, getAuthTables, logger } from "better-auth";
|
|
@@ -96,6 +96,91 @@ function resolveClientIpFromHeaders(headers, ipAddressHeaders) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
//#endregion
|
|
99
|
+
//#region ../utils/dist/url.mjs
|
|
100
|
+
/**
|
|
101
|
+
* URL helpers: normalization, SSRF-safe parsing/validation, and path/query segments.
|
|
102
|
+
*/
|
|
103
|
+
const HTTP_PROTOCOLS = ["http:", "https:"];
|
|
104
|
+
function isAllowedProtocol(protocol, allowedProtocols) {
|
|
105
|
+
return (allowedProtocols?.length ? allowedProtocols : HTTP_PROTOCOLS).includes(protocol);
|
|
106
|
+
}
|
|
107
|
+
/** Normalizes a base URL and path by trimming slashes and joining them. */
|
|
108
|
+
function normalizeUrl(base, path) {
|
|
109
|
+
return `${base.replace(/\/+$/, "")}${path ? `/${path.replace(/^\/+|\/+$/g, "")}` : ""}`;
|
|
110
|
+
}
|
|
111
|
+
function normalizePath(path) {
|
|
112
|
+
const trimmed = path.trim();
|
|
113
|
+
if (!trimmed) throw new Error("Path must be a non-empty string");
|
|
114
|
+
return `/${trimmed.replace(/^\/+|\/+$/g, "")}`;
|
|
115
|
+
}
|
|
116
|
+
/** Normalizes a base URL and path into separate parts and a joined URL. */
|
|
117
|
+
function resolveBaseUrlPath(baseURL, basePath) {
|
|
118
|
+
const cleanBase = baseURL.trim().replace(/\/+$/, "");
|
|
119
|
+
const resolvedBasePath = normalizePath(basePath);
|
|
120
|
+
return {
|
|
121
|
+
baseURL: cleanBase,
|
|
122
|
+
basePath: resolvedBasePath,
|
|
123
|
+
url: normalizeUrl(cleanBase, resolvedBasePath)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Falls back when basePath is null, undefined, or blank/whitespace. */
|
|
127
|
+
function resolveProjectBasePath(basePath) {
|
|
128
|
+
return basePath?.trim() || "/api/auth";
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Normalized project auth base URL + path (blank basePath → default).
|
|
132
|
+
* If `baseURL` already has a non-root pathname, that path wins over `basePath`
|
|
133
|
+
* (Better Auth path-in-baseURL form).
|
|
134
|
+
*/
|
|
135
|
+
function resolveProjectAuthBase(baseURL, basePath) {
|
|
136
|
+
try {
|
|
137
|
+
const url = new URL(baseURL);
|
|
138
|
+
const pathname = url.pathname.replace(/\/+$/, "") || "";
|
|
139
|
+
if (pathname && pathname !== "/") return resolveBaseUrlPath(url.origin, pathname);
|
|
140
|
+
return resolveBaseUrlPath(url.origin, resolveProjectBasePath(basePath));
|
|
141
|
+
} catch {
|
|
142
|
+
return resolveBaseUrlPath(baseURL, resolveProjectBasePath(basePath));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function toAllowedOriginSet(allowedOrigins) {
|
|
146
|
+
const set = /* @__PURE__ */ new Set();
|
|
147
|
+
for (const raw of allowedOrigins) try {
|
|
148
|
+
const parsed = new URL(raw.trim());
|
|
149
|
+
if (isAllowedProtocol(parsed.protocol, HTTP_PROTOCOLS)) set.add(parsed.origin);
|
|
150
|
+
} catch {}
|
|
151
|
+
return set;
|
|
152
|
+
}
|
|
153
|
+
function isAllowedOrigin(origin, allowedOrigins) {
|
|
154
|
+
if (!allowedOrigins.length) return false;
|
|
155
|
+
return toAllowedOriginSet(allowedOrigins).has(origin);
|
|
156
|
+
}
|
|
157
|
+
function safeResolveUrl(raw, baseURL) {
|
|
158
|
+
const trim = raw.trim();
|
|
159
|
+
if (!trim) return null;
|
|
160
|
+
if (trim.startsWith("//") || trim.includes("\\")) return null;
|
|
161
|
+
if (/[\u0000-\u001F\u007F]/.test(trim)) return null;
|
|
162
|
+
try {
|
|
163
|
+
if (trim.startsWith("/")) return new URL(trim, baseURL);
|
|
164
|
+
return new URL(trim);
|
|
165
|
+
} catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function getSafeUniqueOrigins(urls) {
|
|
170
|
+
return [...toAllowedOriginSet(urls)];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Returns an absolute URL only if its protocol and origin are allowed (after
|
|
174
|
+
* resolving relative paths against `baseURL`). Otherwise returns `null`.
|
|
175
|
+
*/
|
|
176
|
+
function safeUrlParse(input) {
|
|
177
|
+
if (input.raw == null || input.raw.trim() === "") return null;
|
|
178
|
+
const target = safeResolveUrl(input.raw, input.baseURL);
|
|
179
|
+
if (!target || !isAllowedProtocol(target.protocol, input.allowedProtocols)) return null;
|
|
180
|
+
if (!isAllowedOrigin(target.origin, input.allowedOrigins ?? [])) return null;
|
|
181
|
+
return target.href;
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
99
184
|
//#region src/events/constants.ts
|
|
100
185
|
const EVENT_TYPES = {
|
|
101
186
|
USER_CREATED: "user_created",
|
|
@@ -2547,7 +2632,7 @@ const sentinel = (options) => {
|
|
|
2547
2632
|
id: "sentinel",
|
|
2548
2633
|
init(ctx) {
|
|
2549
2634
|
activityTrackingEnabled = (ctx.getPlugin("dash")?.options)?.activityTracking?.enabled === true;
|
|
2550
|
-
const basePath =
|
|
2635
|
+
const basePath = ctx.baseURL ? resolveProjectAuthBase(ctx.baseURL, ctx.options.basePath).basePath : resolveProjectBasePath(ctx.options.basePath);
|
|
2551
2636
|
const ipHeaders = ctx.options.advanced?.ipAddress?.ipAddressHeaders || null;
|
|
2552
2637
|
const disableIpTracking = ctx.options.advanced?.ipAddress?.disableIpTracking === true;
|
|
2553
2638
|
/**
|
|
@@ -4267,6 +4352,66 @@ const jwtValidateMiddleware = (options) => {
|
|
|
4267
4352
|
});
|
|
4268
4353
|
};
|
|
4269
4354
|
//#endregion
|
|
4355
|
+
//#region src/lib/safe-url.ts
|
|
4356
|
+
function isSafeHttpUrl(value) {
|
|
4357
|
+
try {
|
|
4358
|
+
const url = new URL(value);
|
|
4359
|
+
return (url.protocol === "http:" || url.protocol === "https:") && url.username === "" && url.password === "";
|
|
4360
|
+
} catch {
|
|
4361
|
+
return false;
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
/** http(s) absolute URL without embedded credentials. */
|
|
4365
|
+
const safeUrlSchema = z.string().trim().min(1).pipe(z.url().refine(isSafeHttpUrl, { message: "URL must be a valid http(s) URL without credentials" })).transform((value) => new URL(value).href);
|
|
4366
|
+
const optionalSafeUrlSchema = z.union([safeUrlSchema, z.literal("")]).optional();
|
|
4367
|
+
/**
|
|
4368
|
+
* Request-scoped full auth URL (origin + mount). Prefer over `options.baseURL`,
|
|
4369
|
+
* which is origin-only or a dynamic config object.
|
|
4370
|
+
* BA resolves this before endpoint handlers; missing value is a programming error.
|
|
4371
|
+
*/
|
|
4372
|
+
function getAuthBaseUrl(ctx) {
|
|
4373
|
+
const resolved = ctx.context.baseURL?.trim();
|
|
4374
|
+
if (!resolved) throw new APIError$1("INTERNAL_SERVER_ERROR", { message: "Auth base URL is not resolved for this request." });
|
|
4375
|
+
return resolved;
|
|
4376
|
+
}
|
|
4377
|
+
/** App origin for post-auth redirects (not the auth mount path). */
|
|
4378
|
+
function getAuthOrigin(ctx) {
|
|
4379
|
+
return new URL(getAuthBaseUrl(ctx)).origin;
|
|
4380
|
+
}
|
|
4381
|
+
function getAllowedAuthOrigins(ctx, extraOrigins = []) {
|
|
4382
|
+
return getSafeUniqueOrigins([
|
|
4383
|
+
getAuthBaseUrl(ctx),
|
|
4384
|
+
...ctx.context.trustedOrigins ?? [],
|
|
4385
|
+
...extraOrigins
|
|
4386
|
+
]);
|
|
4387
|
+
}
|
|
4388
|
+
/**
|
|
4389
|
+
* Resolve a redirect against the auth server's base URL and trusted origins.
|
|
4390
|
+
* Falls back when the target origin is not allowed.
|
|
4391
|
+
*/
|
|
4392
|
+
function resolveTrustedAuthRedirectUrl(ctx, raw, fallback) {
|
|
4393
|
+
return safeUrlParse({
|
|
4394
|
+
raw,
|
|
4395
|
+
baseURL: getAuthBaseUrl(ctx),
|
|
4396
|
+
allowedOrigins: getAllowedAuthOrigins(ctx)
|
|
4397
|
+
}) ?? fallback;
|
|
4398
|
+
}
|
|
4399
|
+
/** Returns a trusted redirect URL or null when the origin is not allowed. */
|
|
4400
|
+
function parseTrustedAuthRedirectUrl(ctx, raw) {
|
|
4401
|
+
return safeUrlParse({
|
|
4402
|
+
raw,
|
|
4403
|
+
baseURL: getAuthBaseUrl(ctx),
|
|
4404
|
+
allowedOrigins: getAllowedAuthOrigins(ctx)
|
|
4405
|
+
});
|
|
4406
|
+
}
|
|
4407
|
+
const TRUSTED_CALLBACK_URL_ERROR = "Callback URL must use http(s) and match the auth base URL or a trusted origin.";
|
|
4408
|
+
/** Rejects email/redirect callbacks whose origin is not on the auth allowlist. */
|
|
4409
|
+
function requireTrustedAuthCallbackUrl(ctx, raw) {
|
|
4410
|
+
const trusted = parseTrustedAuthRedirectUrl(ctx, raw);
|
|
4411
|
+
if (!trusted) throw new APIError$1("BAD_REQUEST", { message: TRUSTED_CALLBACK_URL_ERROR });
|
|
4412
|
+
return trusted;
|
|
4413
|
+
}
|
|
4414
|
+
//#endregion
|
|
4270
4415
|
//#region ../utils/dist/semver.mjs
|
|
4271
4416
|
/**
|
|
4272
4417
|
* Parses a semver string into components.
|
|
@@ -4337,6 +4482,7 @@ const getConfig = (options) => {
|
|
|
4337
4482
|
}, async (ctx) => {
|
|
4338
4483
|
const advancedOptions = ctx.context.options.advanced;
|
|
4339
4484
|
const organizationPlugin = ctx.context.getPlugin("organization");
|
|
4485
|
+
const authBase = resolveProjectAuthBase(getAuthBaseUrl(ctx), ctx.context.options.basePath);
|
|
4340
4486
|
return redactDashSettings({
|
|
4341
4487
|
version: ctx.context.version || null,
|
|
4342
4488
|
socialProviders: Object.keys(ctx.context.options.socialProviders || {}),
|
|
@@ -4406,8 +4552,8 @@ const getConfig = (options) => {
|
|
|
4406
4552
|
deleteUserEnabled: !!ctx.context.options.user?.deleteUser?.enabled,
|
|
4407
4553
|
modelName: ctx.context.options.user?.modelName
|
|
4408
4554
|
},
|
|
4409
|
-
baseURL:
|
|
4410
|
-
basePath:
|
|
4555
|
+
baseURL: authBase.baseURL,
|
|
4556
|
+
basePath: authBase.basePath,
|
|
4411
4557
|
emailVerification: { sendVerificationEmailEnabled: !!ctx.context.options.emailVerification?.sendVerificationEmail },
|
|
4412
4558
|
insights: {
|
|
4413
4559
|
hasDatabase: !!ctx.context.options.database,
|
|
@@ -4601,7 +4747,7 @@ const directorySyncClaimsSchema = z.object({
|
|
|
4601
4747
|
setupOperationId: z.string().trim().min(16).max(255).optional()
|
|
4602
4748
|
});
|
|
4603
4749
|
function getScimEndpoint$1(baseUrl) {
|
|
4604
|
-
return
|
|
4750
|
+
return normalizeUrl(baseUrl, "scim/v2");
|
|
4605
4751
|
}
|
|
4606
4752
|
function assertDirectorySyncClaims(ctx, organizationId) {
|
|
4607
4753
|
const claims = ctx.context.payload;
|
|
@@ -5503,7 +5649,7 @@ async function scimProviderExists(ctx, organizationId, providerId) {
|
|
|
5503
5649
|
}) != null;
|
|
5504
5650
|
}
|
|
5505
5651
|
function getScimEndpoint(baseUrl) {
|
|
5506
|
-
return
|
|
5652
|
+
return normalizeUrl(baseUrl, "scim/v2");
|
|
5507
5653
|
}
|
|
5508
5654
|
function resolveMode(ctx) {
|
|
5509
5655
|
return resolveDirectorySyncMode(getSCIMPlugin(ctx), isManagedDirectorySyncEnabled(ctx));
|
|
@@ -6062,105 +6208,6 @@ function allowsPasswordlessInviteComplete(authMode) {
|
|
|
6062
6208
|
return authMode === "create_with_session";
|
|
6063
6209
|
}
|
|
6064
6210
|
//#endregion
|
|
6065
|
-
//#region ../utils/dist/url.mjs
|
|
6066
|
-
/**
|
|
6067
|
-
* URL helpers: normalization, SSRF-safe parsing/validation, and path/query segments.
|
|
6068
|
-
*/
|
|
6069
|
-
const HTTP_PROTOCOLS = ["http:", "https:"];
|
|
6070
|
-
function isAllowedProtocol(protocol, allowedProtocols) {
|
|
6071
|
-
return (allowedProtocols?.length ? allowedProtocols : HTTP_PROTOCOLS).includes(protocol);
|
|
6072
|
-
}
|
|
6073
|
-
function toAllowedOriginSet(allowedOrigins) {
|
|
6074
|
-
const set = /* @__PURE__ */ new Set();
|
|
6075
|
-
for (const raw of allowedOrigins) try {
|
|
6076
|
-
const parsed = new URL(raw.trim());
|
|
6077
|
-
if (isAllowedProtocol(parsed.protocol, HTTP_PROTOCOLS)) set.add(parsed.origin);
|
|
6078
|
-
} catch {}
|
|
6079
|
-
return set;
|
|
6080
|
-
}
|
|
6081
|
-
function isAllowedOrigin(origin, allowedOrigins) {
|
|
6082
|
-
if (!allowedOrigins.length) return false;
|
|
6083
|
-
return toAllowedOriginSet(allowedOrigins).has(origin);
|
|
6084
|
-
}
|
|
6085
|
-
function safeResolveUrl(raw, baseURL) {
|
|
6086
|
-
const trim = raw.trim();
|
|
6087
|
-
if (!trim) return null;
|
|
6088
|
-
if (trim.startsWith("//") || trim.includes("\\")) return null;
|
|
6089
|
-
if (/[\u0000-\u001F\u007F]/.test(trim)) return null;
|
|
6090
|
-
try {
|
|
6091
|
-
if (trim.startsWith("/")) return new URL(trim, baseURL);
|
|
6092
|
-
return new URL(trim);
|
|
6093
|
-
} catch {
|
|
6094
|
-
return null;
|
|
6095
|
-
}
|
|
6096
|
-
}
|
|
6097
|
-
function getSafeUniqueOrigins(urls) {
|
|
6098
|
-
return [...toAllowedOriginSet(urls)];
|
|
6099
|
-
}
|
|
6100
|
-
/**
|
|
6101
|
-
* Returns an absolute URL only if its protocol and origin are allowed (after
|
|
6102
|
-
* resolving relative paths against `baseURL`). Otherwise returns `null`.
|
|
6103
|
-
*/
|
|
6104
|
-
function safeUrlParse(input) {
|
|
6105
|
-
if (input.raw == null || input.raw.trim() === "") return null;
|
|
6106
|
-
const target = safeResolveUrl(input.raw, input.baseURL);
|
|
6107
|
-
if (!target || !isAllowedProtocol(target.protocol, input.allowedProtocols)) return null;
|
|
6108
|
-
if (!isAllowedOrigin(target.origin, input.allowedOrigins ?? [])) return null;
|
|
6109
|
-
return target.href;
|
|
6110
|
-
}
|
|
6111
|
-
//#endregion
|
|
6112
|
-
//#region src/lib/safe-url.ts
|
|
6113
|
-
function isSafeHttpUrl(value) {
|
|
6114
|
-
try {
|
|
6115
|
-
const url = new URL(value);
|
|
6116
|
-
return (url.protocol === "http:" || url.protocol === "https:") && url.username === "" && url.password === "";
|
|
6117
|
-
} catch {
|
|
6118
|
-
return false;
|
|
6119
|
-
}
|
|
6120
|
-
}
|
|
6121
|
-
/** http(s) absolute URL without embedded credentials. */
|
|
6122
|
-
const safeUrlSchema = z.string().trim().min(1).pipe(z.url().refine(isSafeHttpUrl, { message: "URL must be a valid http(s) URL without credentials" })).transform((value) => new URL(value).href);
|
|
6123
|
-
const optionalSafeUrlSchema = z.union([safeUrlSchema, z.literal("")]).optional();
|
|
6124
|
-
function getAuthBaseUrl(ctx) {
|
|
6125
|
-
const baseURL = ctx.context.options.baseURL;
|
|
6126
|
-
return typeof baseURL === "string" && baseURL.trim() ? baseURL : "/";
|
|
6127
|
-
}
|
|
6128
|
-
function getAllowedAuthOrigins(ctx, extraOrigins = []) {
|
|
6129
|
-
const baseURL = getAuthBaseUrl(ctx);
|
|
6130
|
-
const trusted = ctx.context.options.trustedOrigins;
|
|
6131
|
-
return getSafeUniqueOrigins([
|
|
6132
|
-
baseURL,
|
|
6133
|
-
...Array.isArray(trusted) ? trusted.filter((origin) => typeof origin === "string") : [],
|
|
6134
|
-
...extraOrigins
|
|
6135
|
-
]);
|
|
6136
|
-
}
|
|
6137
|
-
/**
|
|
6138
|
-
* Resolve a redirect against the auth server's base URL and trusted origins.
|
|
6139
|
-
* Falls back when the target origin is not allowed.
|
|
6140
|
-
*/
|
|
6141
|
-
function resolveTrustedAuthRedirectUrl(ctx, raw, fallback) {
|
|
6142
|
-
return safeUrlParse({
|
|
6143
|
-
raw,
|
|
6144
|
-
baseURL: getAuthBaseUrl(ctx),
|
|
6145
|
-
allowedOrigins: getAllowedAuthOrigins(ctx)
|
|
6146
|
-
}) ?? fallback;
|
|
6147
|
-
}
|
|
6148
|
-
/** Returns a trusted redirect URL or null when the origin is not allowed. */
|
|
6149
|
-
function parseTrustedAuthRedirectUrl(ctx, raw) {
|
|
6150
|
-
return safeUrlParse({
|
|
6151
|
-
raw,
|
|
6152
|
-
baseURL: getAuthBaseUrl(ctx),
|
|
6153
|
-
allowedOrigins: getAllowedAuthOrigins(ctx)
|
|
6154
|
-
});
|
|
6155
|
-
}
|
|
6156
|
-
const TRUSTED_CALLBACK_URL_ERROR = "Callback URL must use http(s) and match the auth base URL or a trusted origin.";
|
|
6157
|
-
/** Rejects email/redirect callbacks whose origin is not on the auth allowlist. */
|
|
6158
|
-
function requireTrustedAuthCallbackUrl(ctx, raw) {
|
|
6159
|
-
const trusted = parseTrustedAuthRedirectUrl(ctx, raw);
|
|
6160
|
-
if (!trusted) throw new APIError$1("BAD_REQUEST", { message: TRUSTED_CALLBACK_URL_ERROR });
|
|
6161
|
-
return trusted;
|
|
6162
|
-
}
|
|
6163
|
-
//#endregion
|
|
6164
6211
|
//#region src/routes/invitations/index.ts
|
|
6165
6212
|
async function verifyPendingInvitation(token, $api, ctx) {
|
|
6166
6213
|
const { data: invitation, error } = await $api("/api/internal/invitations/verify", {
|
|
@@ -6206,7 +6253,7 @@ async function executePlatformInvitationCompletion(ctx, $api, invitation, args)
|
|
|
6206
6253
|
user: existingUser
|
|
6207
6254
|
});
|
|
6208
6255
|
}
|
|
6209
|
-
return { redirectUrl: resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl,
|
|
6256
|
+
return { redirectUrl: resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthOrigin(ctx)) };
|
|
6210
6257
|
}
|
|
6211
6258
|
if (!password && !allowsPasswordlessInviteComplete(invitation.authMode)) throw new APIError("BAD_REQUEST", { message: "Password is required to complete this invitation." });
|
|
6212
6259
|
const userPayload = {
|
|
@@ -6237,7 +6284,7 @@ async function executePlatformInvitationCompletion(ctx, $api, invitation, args)
|
|
|
6237
6284
|
user
|
|
6238
6285
|
});
|
|
6239
6286
|
}
|
|
6240
|
-
return { redirectUrl: resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl,
|
|
6287
|
+
return { redirectUrl: resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthOrigin(ctx)) };
|
|
6241
6288
|
}
|
|
6242
6289
|
/**
|
|
6243
6290
|
* Accept invitation endpoint
|
|
@@ -6270,14 +6317,14 @@ const acceptInvitation = (options) => {
|
|
|
6270
6317
|
user: existingUser
|
|
6271
6318
|
});
|
|
6272
6319
|
}
|
|
6273
|
-
const redirectUrl = resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl,
|
|
6320
|
+
const redirectUrl = resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthOrigin(ctx));
|
|
6274
6321
|
return ctx.redirect(redirectUrl);
|
|
6275
6322
|
}
|
|
6276
6323
|
if (usesPlatformInviteAcceptFlow(invitation.authMode)) {
|
|
6277
6324
|
const platformUrl = options.apiUrl || INFRA_API_URL;
|
|
6278
6325
|
const acceptPageUrl = new URL("/invite/accept", platformUrl);
|
|
6279
6326
|
acceptPageUrl.searchParams.set("token", token);
|
|
6280
|
-
const callbackUrl =
|
|
6327
|
+
const callbackUrl = normalizeUrl(ctx.context.baseURL, "dash/complete-invitation");
|
|
6281
6328
|
acceptPageUrl.searchParams.set("callback", callbackUrl);
|
|
6282
6329
|
return ctx.redirect(acceptPageUrl.toString());
|
|
6283
6330
|
}
|
|
@@ -6305,7 +6352,7 @@ const acceptInvitation = (options) => {
|
|
|
6305
6352
|
user
|
|
6306
6353
|
});
|
|
6307
6354
|
}
|
|
6308
|
-
const redirectUrl = resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl,
|
|
6355
|
+
const redirectUrl = resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthOrigin(ctx));
|
|
6309
6356
|
return ctx.redirect(redirectUrl);
|
|
6310
6357
|
});
|
|
6311
6358
|
};
|
|
@@ -6385,7 +6432,7 @@ const completeInvitationSocial = (options) => {
|
|
|
6385
6432
|
if (sessionCookieToken) await ctx.context.internalAdapter.deleteSession(sessionCookieToken);
|
|
6386
6433
|
deleteSessionCookie(ctx);
|
|
6387
6434
|
}
|
|
6388
|
-
return ctx.redirect(resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl,
|
|
6435
|
+
return ctx.redirect(resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthOrigin(ctx)));
|
|
6389
6436
|
});
|
|
6390
6437
|
};
|
|
6391
6438
|
/**
|
|
@@ -8439,7 +8486,7 @@ async function resolveSAMLConfig(samlConfig, providerId, baseURL, ctx) {
|
|
|
8439
8486
|
const ba17OrLater = isBetterAuth17OrLater(ctx.context.version);
|
|
8440
8487
|
if (saml17OrNewer && !resolvedEntryPoint) throw ctx.error("BAD_REQUEST", { message: "SAML entry point URL is required; provide entryPoint or IdP metadata with SingleSignOnService Location" });
|
|
8441
8488
|
if (ba17OrLater && !idpMetadataXml && !samlConfig.entityId?.trim()) throw ctx.error("BAD_REQUEST", { message: "IdP entity ID is required when IdP metadata XML is not provided" });
|
|
8442
|
-
const spIssuer = `${baseURL
|
|
8489
|
+
const spIssuer = `${normalizeUrl(baseURL, "sso/saml2/sp/metadata")}?providerId=${providerId}`;
|
|
8443
8490
|
const idpMetadata = idpMetadataXml ? { metadata: idpMetadataXml } : {
|
|
8444
8491
|
...ba17OrLater ? { entityID: samlConfig.entityId } : {},
|
|
8445
8492
|
...samlConfig.entryPoint ? { singleSignOnService: [{
|
|
@@ -8457,7 +8504,7 @@ async function resolveSAMLConfig(samlConfig, providerId, baseURL, ctx) {
|
|
|
8457
8504
|
entryPoint: resolvedEntryPoint,
|
|
8458
8505
|
...samlConfig.cert ? { cert: samlConfig.cert } : {}
|
|
8459
8506
|
} : {
|
|
8460
|
-
callbackUrl:
|
|
8507
|
+
callbackUrl: normalizeUrl(baseURL, `sso/saml2/sp/acs/${providerId}`),
|
|
8461
8508
|
spMetadata: {},
|
|
8462
8509
|
entryPoint: resolvedEntryPoint ?? "",
|
|
8463
8510
|
cert: samlConfig.cert ?? ""
|
package/dist/native.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { o as createKV, t as bytesToHex } from "./crypto-
|
|
2
|
-
import { a as resolveSentinelClientIdentifyUrl, c as identify, i as solvePoWChallenge, l as dashClient, n as decodePoWChallenge, o as resolveIdentifyClientRetry, r as encodePoWSolution, t as createPowRetryTimeout } from "./pow-retry-
|
|
1
|
+
import { o as createKV, t as bytesToHex } from "./crypto-DDadesq-.mjs";
|
|
2
|
+
import { a as resolveSentinelClientIdentifyUrl, c as identify, i as solvePoWChallenge, l as dashClient, n as decodePoWChallenge, o as resolveIdentifyClientRetry, r as encodePoWSolution, t as createPowRetryTimeout } from "./pow-retry-DRUkPcBn.mjs";
|
|
3
3
|
import { env } from "@better-auth/core/env";
|
|
4
4
|
import { Dimensions, InteractionManager, PixelRatio, Platform } from "react-native";
|
|
5
5
|
//#region src/sentinel/native/components.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as randomBytes, n as hash, t as bytesToHex } from "./crypto-
|
|
1
|
+
import { i as randomBytes, n as hash, t as bytesToHex } from "./crypto-DDadesq-.mjs";
|
|
2
2
|
//#region src/dash-client.ts
|
|
3
3
|
function resolveDashUserId(input, options) {
|
|
4
4
|
return input.userId || options?.resolveUserId?.({
|