better-auth 1.6.2 → 1.6.4
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/index.d.mts +0 -2
- package/dist/api/routes/account.mjs +1 -1
- package/dist/api/routes/callback.mjs +1 -1
- package/dist/api/routes/email-verification.mjs +1 -1
- package/dist/api/routes/password.mjs +1 -1
- package/dist/api/routes/session.d.mts +0 -1
- package/dist/api/routes/session.mjs +3 -4
- package/dist/api/routes/sign-in.mjs +1 -1
- package/dist/api/to-auth-endpoints.mjs +27 -3
- package/dist/auth/base.mjs +5 -24
- package/dist/client/plugins/index.d.mts +2 -2
- package/dist/client/query.mjs +4 -4
- package/dist/context/create-context.mjs +2 -2
- package/dist/context/helpers.mjs +61 -3
- package/dist/cookies/index.mjs +3 -3
- package/dist/crypto/index.mjs +2 -2
- package/dist/db/index.mjs +1 -1
- package/dist/db/internal-adapter.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/package.mjs +1 -1
- package/dist/plugins/admin/admin.d.mts +1 -3
- package/dist/plugins/admin/routes.mjs +2 -8
- package/dist/plugins/device-authorization/routes.mjs +1 -1
- package/dist/plugins/email-otp/routes.mjs +1 -1
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/jwt/utils.mjs +1 -1
- package/dist/plugins/mcp/index.mjs +20 -8
- package/dist/plugins/oauth-proxy/index.mjs +1 -1
- package/dist/plugins/oidc-provider/index.mjs +2 -2
- package/dist/plugins/organization/organization.d.mts +1 -3
- package/dist/plugins/organization/organization.mjs +1 -7
- package/dist/plugins/organization/routes/crud-invites.mjs +1 -1
- package/dist/plugins/organization/routes/crud-org.mjs +1 -1
- package/dist/plugins/organization/routes/crud-team.mjs +1 -1
- package/dist/plugins/phone-number/routes.mjs +1 -1
- package/dist/plugins/two-factor/backup-codes/index.d.mts +2 -1
- package/dist/plugins/two-factor/backup-codes/index.mjs +12 -17
- package/dist/plugins/two-factor/client.d.mts +1 -1
- package/dist/plugins/two-factor/index.d.mts +2 -2
- package/dist/test-utils/test-instance.d.mts +0 -6
- package/dist/test-utils/test-instance.mjs +7 -1
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/url.d.mts +22 -15
- package/dist/utils/url.mjs +54 -28
- package/package.json +11 -11
package/dist/api/index.d.mts
CHANGED
|
@@ -249,7 +249,6 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
|
|
|
249
249
|
"application/json": {
|
|
250
250
|
schema: {
|
|
251
251
|
type: "object";
|
|
252
|
-
nullable: boolean;
|
|
253
252
|
properties: {
|
|
254
253
|
session: {
|
|
255
254
|
$ref: string;
|
|
@@ -2239,7 +2238,6 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
|
|
|
2239
2238
|
"application/json": {
|
|
2240
2239
|
schema: {
|
|
2241
2240
|
type: "object";
|
|
2242
|
-
nullable: boolean;
|
|
2243
2241
|
properties: {
|
|
2244
2242
|
session: {
|
|
2245
2243
|
$ref: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseAccountOutput } from "../../db/schema.mjs";
|
|
2
|
-
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
3
2
|
import { getAccountCookie, setAccountCookie } from "../../cookies/session-store.mjs";
|
|
3
|
+
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
4
4
|
import { generateState } from "../../oauth2/state.mjs";
|
|
5
5
|
import { decryptOAuthToken, setTokenUtil } from "../../oauth2/utils.mjs";
|
|
6
6
|
import { freshSessionMiddleware, getSessionFromCtx, sessionMiddleware } from "./session.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
2
1
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
2
|
+
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
3
3
|
import { parseState } from "../../oauth2/state.mjs";
|
|
4
4
|
import { setTokenUtil } from "../../oauth2/utils.mjs";
|
|
5
5
|
import { handleOAuthUserInfo } from "../../oauth2/link-account.mjs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { originCheck } from "../middlewares/origin-check.mjs";
|
|
2
|
-
import { parseUserOutput } from "../../db/schema.mjs";
|
|
3
2
|
import { signJWT } from "../../crypto/jwt.mjs";
|
|
3
|
+
import { parseUserOutput } from "../../db/schema.mjs";
|
|
4
4
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
5
5
|
import { getSessionFromCtx } from "./session.mjs";
|
|
6
6
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
@@ -82,7 +82,7 @@ const requestPasswordReset = createAuthEndpoint("/request-password-reset", {
|
|
|
82
82
|
});
|
|
83
83
|
const requestPasswordResetCallback = createAuthEndpoint("/reset-password/:token", {
|
|
84
84
|
method: "GET",
|
|
85
|
-
operationId: "
|
|
85
|
+
operationId: "resetPasswordCallback",
|
|
86
86
|
query: z.object({ callbackURL: z.string().meta({ description: "The URL to redirect the user to reset their password" }) }),
|
|
87
87
|
use: [originCheck((ctx) => ctx.query.callbackURL)],
|
|
88
88
|
metadata: { openapi: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isAPIError } from "../../utils/is-api-error.mjs";
|
|
2
|
-
import { getDate } from "../../utils/date.mjs";
|
|
3
|
-
import { parseSessionOutput, parseUserOutput } from "../../db/schema.mjs";
|
|
4
2
|
import { symmetricDecodeJWT, verifyJWT } from "../../crypto/jwt.mjs";
|
|
3
|
+
import { parseSessionOutput, parseUserOutput } from "../../db/schema.mjs";
|
|
4
|
+
import { getDate } from "../../utils/date.mjs";
|
|
5
5
|
import { getChunkedCookie, getSessionQuerySchema } from "../../cookies/session-store.mjs";
|
|
6
6
|
import { deleteSessionCookie, expireCookie, setCookieCache, setSessionCookie } from "../../cookies/index.mjs";
|
|
7
7
|
import { getShouldSkipSessionRefresh } from "../state/should-session-refresh.mjs";
|
|
@@ -24,8 +24,7 @@ const getSession = () => createAuthEndpoint("/get-session", {
|
|
|
24
24
|
responses: { "200": {
|
|
25
25
|
description: "Success",
|
|
26
26
|
content: { "application/json": { schema: {
|
|
27
|
-
type: "object",
|
|
28
|
-
nullable: true,
|
|
27
|
+
type: ["object", "null"],
|
|
29
28
|
properties: {
|
|
30
29
|
session: { $ref: "#/components/schemas/Session" },
|
|
31
30
|
user: { $ref: "#/components/schemas/User" }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formCsrfMiddleware } from "../middlewares/origin-check.mjs";
|
|
2
2
|
import { parseUserOutput } from "../../db/schema.mjs";
|
|
3
|
-
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
4
3
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
4
|
+
import { getAwaitableValue } from "../../context/helpers.mjs";
|
|
5
5
|
import { generateState } from "../../oauth2/state.mjs";
|
|
6
6
|
import { handleOAuthUserInfo } from "../../oauth2/link-account.mjs";
|
|
7
7
|
import { createEmailVerificationToken } from "./email-verification.mjs";
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { isAPIError } from "../utils/is-api-error.mjs";
|
|
2
|
+
import { isDynamicBaseURLConfig, isRequestLike } from "../utils/url.mjs";
|
|
3
|
+
import { pickSource, resolveDynamicTrustedProxyHeaders, resolveRequestContext } from "../context/helpers.mjs";
|
|
2
4
|
import { hasRequestState, runWithEndpointContext, runWithRequestState } from "@better-auth/core/context";
|
|
3
5
|
import { shouldPublishLog } from "@better-auth/core/env";
|
|
4
|
-
import { APIError } from "@better-auth/core/error";
|
|
6
|
+
import { APIError, BetterAuthError } from "@better-auth/core/error";
|
|
5
7
|
import { createDefu } from "defu";
|
|
6
8
|
import { ATTR_CONTEXT, ATTR_HOOK_TYPE, ATTR_HTTP_ROUTE, ATTR_OPERATION_ID, withSpan } from "@better-auth/core/instrumentation";
|
|
7
9
|
import { kAPIErrorHeaderSymbol, toResponse } from "better-call";
|
|
@@ -18,6 +20,27 @@ function getOperationId(endpoint, key) {
|
|
|
18
20
|
const opts = endpoint.options;
|
|
19
21
|
return opts.operationId ?? opts.metadata?.openapi?.operationId ?? key;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the per-call `AuthContext` for endpoints with a dynamic `baseURL`.
|
|
25
|
+
*
|
|
26
|
+
* - `rawCtx.baseURL` already set: HTTP handler rehydrated upstream; return as-is.
|
|
27
|
+
* - Direct `auth.api` call with a source or a configured `fallback`: resolve here.
|
|
28
|
+
* - Neither: throw `APIError` with a helpful message. Leaving `baseURL = ""`
|
|
29
|
+
* would let plugins build `new URL("")` and crash cryptically downstream.
|
|
30
|
+
*/
|
|
31
|
+
async function resolveDynamicContext(rawCtx, input) {
|
|
32
|
+
if (rawCtx.baseURL) return rawCtx;
|
|
33
|
+
const source = pickSource(input);
|
|
34
|
+
const config = rawCtx.options.baseURL;
|
|
35
|
+
const hasFallback = isDynamicBaseURLConfig(config) && Boolean(config.fallback);
|
|
36
|
+
if (source === void 0 && !hasFallback) throw new APIError("INTERNAL_SERVER_ERROR", { message: "Dynamic baseURL could not be resolved for this direct auth.api call. Pass `headers: request.headers` (or `request`) to the call, or add `fallback` to your baseURL config." });
|
|
37
|
+
try {
|
|
38
|
+
return await resolveRequestContext(rawCtx, source, resolveDynamicTrustedProxyHeaders(rawCtx.options));
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err instanceof BetterAuthError) throw new APIError("INTERNAL_SERVER_ERROR", { message: err.message });
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
21
44
|
function toAuthEndpoints(endpoints, ctx) {
|
|
22
45
|
const api = {};
|
|
23
46
|
for (const [key, endpoint] of Object.entries(endpoints)) {
|
|
@@ -26,9 +49,10 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
26
49
|
const endpointMethod = endpoint?.options?.method;
|
|
27
50
|
const defaultMethod = Array.isArray(endpointMethod) ? endpointMethod[0] : endpointMethod;
|
|
28
51
|
const run = async () => {
|
|
29
|
-
const
|
|
52
|
+
const rawContext = await ctx;
|
|
30
53
|
const methodName = context?.method ?? context?.request?.method ?? defaultMethod ?? "?";
|
|
31
54
|
const route = endpoint.path ?? "/:virtual";
|
|
55
|
+
const authContext = isDynamicBaseURLConfig(rawContext.options.baseURL) ? await resolveDynamicContext(rawContext, context) : rawContext;
|
|
32
56
|
let internalContext = {
|
|
33
57
|
...context,
|
|
34
58
|
context: {
|
|
@@ -40,7 +64,7 @@ function toAuthEndpoints(endpoints, ctx) {
|
|
|
40
64
|
path: endpoint.path,
|
|
41
65
|
headers: context?.headers ? new Headers(context?.headers) : void 0
|
|
42
66
|
};
|
|
43
|
-
const hasRequest = context?.request
|
|
67
|
+
const hasRequest = isRequestLike(context?.request);
|
|
44
68
|
const shouldReturnResponse = context?.asResponse ?? hasRequest;
|
|
45
69
|
return withSpan(`${methodName} ${route}`, {
|
|
46
70
|
[ATTR_HTTP_ROUTE]: route,
|
package/dist/auth/base.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { getBaseURL, getOrigin, isDynamicBaseURLConfig
|
|
2
|
-
import { getTrustedOrigins, getTrustedProviders } from "../context/helpers.mjs";
|
|
3
|
-
import { createCookieGetter, getCookies } from "../cookies/index.mjs";
|
|
1
|
+
import { getBaseURL, getOrigin, isDynamicBaseURLConfig } from "../utils/url.mjs";
|
|
2
|
+
import { getTrustedOrigins, getTrustedProviders, resolveDynamicTrustedProxyHeaders, resolveRequestContext } from "../context/helpers.mjs";
|
|
4
3
|
import { getEndpoints, router } from "../api/index.mjs";
|
|
5
4
|
import { runWithAdapter } from "@better-auth/core/context";
|
|
6
5
|
import { BASE_ERROR_CODES, BetterAuthError } from "@better-auth/core/error";
|
|
@@ -13,26 +12,8 @@ const createBetterAuth = (options, initFn) => {
|
|
|
13
12
|
const ctx = await authContext;
|
|
14
13
|
const basePath = ctx.options.basePath || "/api/auth";
|
|
15
14
|
let handlerCtx;
|
|
16
|
-
if (isDynamicBaseURLConfig(options.baseURL))
|
|
17
|
-
|
|
18
|
-
const baseURL = resolveBaseURL(options.baseURL, basePath, request);
|
|
19
|
-
if (baseURL) {
|
|
20
|
-
handlerCtx.baseURL = baseURL;
|
|
21
|
-
handlerCtx.options = {
|
|
22
|
-
...ctx.options,
|
|
23
|
-
baseURL: getOrigin(baseURL) || void 0
|
|
24
|
-
};
|
|
25
|
-
} else throw new BetterAuthError("Could not resolve base URL from request. Check your allowedHosts config.");
|
|
26
|
-
const trustedOriginOptions = {
|
|
27
|
-
...handlerCtx.options,
|
|
28
|
-
baseURL: options.baseURL
|
|
29
|
-
};
|
|
30
|
-
handlerCtx.trustedOrigins = await getTrustedOrigins(trustedOriginOptions, request);
|
|
31
|
-
if (options.advanced?.crossSubDomainCookies?.enabled) {
|
|
32
|
-
handlerCtx.authCookies = getCookies(handlerCtx.options);
|
|
33
|
-
handlerCtx.createAuthCookie = createCookieGetter(handlerCtx.options);
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
15
|
+
if (isDynamicBaseURLConfig(options.baseURL)) handlerCtx = await resolveRequestContext(ctx, request, resolveDynamicTrustedProxyHeaders(ctx.options));
|
|
16
|
+
else {
|
|
36
17
|
handlerCtx = ctx;
|
|
37
18
|
if (!ctx.options.baseURL) {
|
|
38
19
|
const baseURL = getBaseURL(void 0, basePath, request, void 0, ctx.options.advanced?.trustedProxyHeaders);
|
|
@@ -42,8 +23,8 @@ const createBetterAuth = (options, initFn) => {
|
|
|
42
23
|
} else throw new BetterAuthError("Could not get base URL from request. Please provide a valid base URL.");
|
|
43
24
|
}
|
|
44
25
|
handlerCtx.trustedOrigins = await getTrustedOrigins(ctx.options, request);
|
|
26
|
+
handlerCtx.trustedProviders = await getTrustedProviders(ctx.options, request);
|
|
45
27
|
}
|
|
46
|
-
handlerCtx.trustedProviders = await getTrustedProviders(handlerCtx.options, request);
|
|
47
28
|
const { handler } = router(handlerCtx, options);
|
|
48
29
|
return runWithAdapter(handlerCtx.adapter, () => handler(request));
|
|
49
30
|
},
|
|
@@ -20,7 +20,7 @@ import { MULTI_SESSION_ERROR_CODES } from "../../plugins/multi-session/error-cod
|
|
|
20
20
|
import { MultiSessionConfig } from "../../plugins/multi-session/index.mjs";
|
|
21
21
|
import { OneTimeTokenOptions } from "../../plugins/one-time-token/index.mjs";
|
|
22
22
|
import { PhoneNumberOptions, UserWithPhoneNumber } from "../../plugins/phone-number/types.mjs";
|
|
23
|
-
import { BackupCodeOptions, backupCode2fa, generateBackupCodes, getBackupCodes, verifyBackupCode } from "../../plugins/two-factor/backup-codes/index.mjs";
|
|
23
|
+
import { BackupCodeOptions, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, verifyBackupCode } from "../../plugins/two-factor/backup-codes/index.mjs";
|
|
24
24
|
import { OTPOptions, otp2fa } from "../../plugins/two-factor/otp/index.mjs";
|
|
25
25
|
import { TOTPOptions, totp2fa } from "../../plugins/two-factor/totp/index.mjs";
|
|
26
26
|
import { TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor } from "../../plugins/two-factor/types.mjs";
|
|
@@ -52,4 +52,4 @@ import { phoneNumberClient } from "../../plugins/phone-number/client.mjs";
|
|
|
52
52
|
import { siweClient } from "../../plugins/siwe/client.mjs";
|
|
53
53
|
import { usernameClient } from "../../plugins/username/client.mjs";
|
|
54
54
|
import { InferServerPlugin } from "./infer-plugin.mjs";
|
|
55
|
-
export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, AdminOptions, AnonymousOptions, AnonymousSession, Auth0Options, AuthorizationQuery, BackupCodeOptions, BaseOAuthProviderOptions, Client, CodeVerificationValue, EMAIL_OTP_ERROR_CODES, ExtractPluginField, GENERIC_OAUTH_ERROR_CODES, GenericOAuthConfig, GenericOAuthOptions, GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, GumroadOptions, HasRequiredKeys, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginFieldFromTuple, InferServerPlugin, InferTeam, Invitation, InvitationInput, InvitationStatus, IsAny, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodClientConfig, LineOptions, MULTI_SESSION_ERROR_CODES, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OIDCMetadata, OIDCOptions, ORGANIZATION_ERROR_CODES, OTPOptions, OidcClientPlugin, OktaOptions, OneTimeTokenOptions, Organization, OrganizationInput, OrganizationRole, OrganizationSchema, OverrideMerge, PHONE_NUMBER_ERROR_CODES, PatreonOptions, PhoneNumberOptions, Prettify, PrettifyDeep, RequiredKeysOf, SessionWithImpersonatedBy, SlackOptions, StripEmptyObjects, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamInput, TeamMember, TeamMemberInput, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UnionToIntersection, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, adminClient, anonymousClient, auth0, backupCode2fa, clientSideHasPermission, customSessionClient, defaultRolesSchema, deviceAuthorizationClient, emailOTPClient, generateBackupCodes, genericOAuthClient, getBackupCodes, gumroad, hubspot, inferAdditionalFields, inferOrgAdditionalFields, invitationSchema, invitationStatus, jwtClient, keycloak, lastLoginMethodClient, line, magicLinkClient, memberSchema, microsoftEntraId, multiSessionClient, oidcClient, okta, oneTapClient, oneTimeTokenClient, organizationClient, organizationRoleSchema, organizationSchema, otp2fa, patreon, phoneNumberClient, roleSchema, schema, siweClient, slack, teamMemberSchema, teamSchema, totp2fa, twoFactorClient, usernameClient, verifyBackupCode };
|
|
55
|
+
export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, AdminOptions, AnonymousOptions, AnonymousSession, Auth0Options, AuthorizationQuery, BackupCodeOptions, BaseOAuthProviderOptions, Client, CodeVerificationValue, EMAIL_OTP_ERROR_CODES, ExtractPluginField, GENERIC_OAUTH_ERROR_CODES, GenericOAuthConfig, GenericOAuthOptions, GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, GumroadOptions, HasRequiredKeys, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginFieldFromTuple, InferServerPlugin, InferTeam, Invitation, InvitationInput, InvitationStatus, IsAny, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodClientConfig, LineOptions, MULTI_SESSION_ERROR_CODES, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OIDCMetadata, OIDCOptions, ORGANIZATION_ERROR_CODES, OTPOptions, OidcClientPlugin, OktaOptions, OneTimeTokenOptions, Organization, OrganizationInput, OrganizationRole, OrganizationSchema, OverrideMerge, PHONE_NUMBER_ERROR_CODES, PatreonOptions, PhoneNumberOptions, Prettify, PrettifyDeep, RequiredKeysOf, SessionWithImpersonatedBy, SlackOptions, StripEmptyObjects, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamInput, TeamMember, TeamMemberInput, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UnionToIntersection, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, adminClient, anonymousClient, auth0, backupCode2fa, clientSideHasPermission, customSessionClient, defaultRolesSchema, deviceAuthorizationClient, emailOTPClient, encodeBackupCodes, generateBackupCodes, genericOAuthClient, getBackupCodes, gumroad, hubspot, inferAdditionalFields, inferOrgAdditionalFields, invitationSchema, invitationStatus, jwtClient, keycloak, lastLoginMethodClient, line, magicLinkClient, memberSchema, microsoftEntraId, multiSessionClient, oidcClient, okta, oneTapClient, oneTimeTokenClient, organizationClient, organizationRoleSchema, organizationSchema, otp2fa, patreon, phoneNumberClient, roleSchema, schema, siweClient, slack, teamMemberSchema, teamSchema, totp2fa, twoFactorClient, usernameClient, verifyBackupCode };
|
package/dist/client/query.mjs
CHANGED
|
@@ -72,15 +72,15 @@ const useAuthQuery = (initializedAtom, path, $fetch, options) => {
|
|
|
72
72
|
});
|
|
73
73
|
};
|
|
74
74
|
initializedAtom = Array.isArray(initializedAtom) ? initializedAtom : [initializedAtom];
|
|
75
|
-
let
|
|
75
|
+
let isInitialized = false;
|
|
76
76
|
for (const initAtom of initializedAtom) initAtom.subscribe(async () => {
|
|
77
77
|
if (isServer()) return;
|
|
78
|
-
if (
|
|
78
|
+
if (isInitialized) await fn();
|
|
79
79
|
else onMount(value, () => {
|
|
80
80
|
const timeoutId = setTimeout(async () => {
|
|
81
|
-
if (!
|
|
81
|
+
if (!isInitialized) {
|
|
82
|
+
isInitialized = true;
|
|
82
83
|
await fn();
|
|
83
|
-
isMounted = true;
|
|
84
84
|
}
|
|
85
85
|
}, 0);
|
|
86
86
|
return () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getBaseURL, isDynamicBaseURLConfig } from "../utils/url.mjs";
|
|
2
2
|
import { matchesOriginPattern } from "../auth/trusted-origins.mjs";
|
|
3
|
-
import { createInternalAdapter } from "../db/internal-adapter.mjs";
|
|
4
3
|
import { isPromise } from "../utils/is-promise.mjs";
|
|
5
|
-
import { getInternalPlugins, getTrustedOrigins, getTrustedProviders, runPluginInit } from "./helpers.mjs";
|
|
6
4
|
import { hashPassword, verifyPassword } from "../crypto/password.mjs";
|
|
7
5
|
import { createCookieGetter, getCookies } from "../cookies/index.mjs";
|
|
6
|
+
import { createInternalAdapter } from "../db/internal-adapter.mjs";
|
|
7
|
+
import { getInternalPlugins, getTrustedOrigins, getTrustedProviders, runPluginInit } from "./helpers.mjs";
|
|
8
8
|
import { checkPassword } from "../utils/password.mjs";
|
|
9
9
|
import { checkEndpointConflicts } from "../api/index.mjs";
|
|
10
10
|
import { DEFAULT_SECRET } from "../utils/constants.mjs";
|
package/dist/context/helpers.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { getBaseURL, isDynamicBaseURLConfig } from "../utils/url.mjs";
|
|
2
|
-
import { createInternalAdapter } from "../db/internal-adapter.mjs";
|
|
1
|
+
import { getBaseURL, getOrigin, isDynamicBaseURLConfig, isRequestLike, resolveBaseURL } from "../utils/url.mjs";
|
|
3
2
|
import { isPromise } from "../utils/is-promise.mjs";
|
|
3
|
+
import { createCookieGetter, getCookies } from "../cookies/index.mjs";
|
|
4
|
+
import { createInternalAdapter } from "../db/internal-adapter.mjs";
|
|
4
5
|
import { env } from "@better-auth/core/env";
|
|
6
|
+
import { BetterAuthError } from "@better-auth/core/error";
|
|
5
7
|
import { defu } from "defu";
|
|
6
8
|
//#region src/context/helpers.ts
|
|
7
9
|
async function runPluginInit(context) {
|
|
@@ -80,6 +82,62 @@ async function getTrustedOrigins(options, request) {
|
|
|
80
82
|
if (envTrustedOrigins) trustedOrigins.push(...envTrustedOrigins.split(","));
|
|
81
83
|
return trustedOrigins.filter((v) => Boolean(v));
|
|
82
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Picks a `Request`-like or `Headers` value from a direct `auth.api` call.
|
|
87
|
+
* Headers are only accepted when they carry a host: without one, host
|
|
88
|
+
* resolution would fall back to `null` and the caller should use `fallback`
|
|
89
|
+
* or pass a `Request` instead.
|
|
90
|
+
*/
|
|
91
|
+
function pickSource(input) {
|
|
92
|
+
if (isRequestLike(input?.request)) return input.request;
|
|
93
|
+
if (!input?.headers) return void 0;
|
|
94
|
+
const headers = input.headers instanceof Headers ? input.headers : new Headers(input.headers);
|
|
95
|
+
if (!headers.has("host") && !headers.has("x-forwarded-host")) return;
|
|
96
|
+
return headers;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns the effective `trustedProxyHeaders` value for dynamic `baseURL`
|
|
100
|
+
* resolution. When the user hasn't set `advanced.trustedProxyHeaders`,
|
|
101
|
+
* proxy headers (`x-forwarded-host` / `x-forwarded-proto`) are trusted by
|
|
102
|
+
* default so deployments behind a reverse proxy work without extra config.
|
|
103
|
+
*/
|
|
104
|
+
function resolveDynamicTrustedProxyHeaders(options) {
|
|
105
|
+
return options.advanced?.trustedProxyHeaders ?? true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Per-request clone with `baseURL`, `trustedOrigins`, `trustedProviders`
|
|
109
|
+
* and cookies rehydrated for the resolved host. Throws `BetterAuthError`
|
|
110
|
+
* when the URL cannot be resolved; callers on the direct-API path convert
|
|
111
|
+
* this to `APIError`.
|
|
112
|
+
*/
|
|
113
|
+
async function resolveRequestContext(ctx, source, trustedProxyHeaders) {
|
|
114
|
+
const dynamicBaseURLConfig = ctx.options.baseURL;
|
|
115
|
+
const baseURL = resolveBaseURL(dynamicBaseURLConfig, ctx.options.basePath || "/api/auth", source, void 0, trustedProxyHeaders);
|
|
116
|
+
if (!baseURL) throw new BetterAuthError("Could not resolve base URL from request. Check your allowedHosts config.");
|
|
117
|
+
const resolved = Object.create(Object.getPrototypeOf(ctx), Object.getOwnPropertyDescriptors(ctx));
|
|
118
|
+
resolved.baseURL = baseURL;
|
|
119
|
+
resolved.options = {
|
|
120
|
+
...ctx.options,
|
|
121
|
+
baseURL: getOrigin(baseURL) || void 0
|
|
122
|
+
};
|
|
123
|
+
const trustedOriginOptions = {
|
|
124
|
+
...resolved.options,
|
|
125
|
+
baseURL: dynamicBaseURLConfig
|
|
126
|
+
};
|
|
127
|
+
const needsRequest = typeof ctx.options.trustedOrigins === "function" || typeof ctx.options.account?.accountLinking?.trustedProviders === "function";
|
|
128
|
+
let callbackRequest;
|
|
129
|
+
if (needsRequest) if (isRequestLike(source)) callbackRequest = source;
|
|
130
|
+
else if (source) callbackRequest = new Request(baseURL, { headers: source });
|
|
131
|
+
else callbackRequest = void 0;
|
|
132
|
+
else callbackRequest = void 0;
|
|
133
|
+
resolved.trustedOrigins = await getTrustedOrigins(trustedOriginOptions, callbackRequest);
|
|
134
|
+
resolved.trustedProviders = await getTrustedProviders(resolved.options, callbackRequest);
|
|
135
|
+
if (ctx.options.advanced?.crossSubDomainCookies?.enabled) {
|
|
136
|
+
resolved.authCookies = getCookies(resolved.options);
|
|
137
|
+
resolved.createAuthCookie = createCookieGetter(resolved.options);
|
|
138
|
+
}
|
|
139
|
+
return resolved;
|
|
140
|
+
}
|
|
83
141
|
async function getAwaitableValue(arr, item) {
|
|
84
142
|
if (!arr) return void 0;
|
|
85
143
|
for (const val of arr) {
|
|
@@ -94,4 +152,4 @@ async function getTrustedProviders(options, request) {
|
|
|
94
152
|
return (await trustedProviders(request) ?? []).filter((v) => Boolean(v));
|
|
95
153
|
}
|
|
96
154
|
//#endregion
|
|
97
|
-
export { getAwaitableValue, getInternalPlugins, getTrustedOrigins, getTrustedProviders, runPluginInit };
|
|
155
|
+
export { getAwaitableValue, getInternalPlugins, getTrustedOrigins, getTrustedProviders, pickSource, resolveDynamicTrustedProxyHeaders, resolveRequestContext, runPluginInit };
|
package/dist/cookies/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { isDynamicBaseURLConfig } from "../utils/url.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { signJWT, symmetricDecodeJWT, symmetricEncodeJWT, verifyJWT } from "../crypto/jwt.mjs";
|
|
3
3
|
import { parseUserOutput } from "../db/schema.mjs";
|
|
4
|
+
import { getDate } from "../utils/date.mjs";
|
|
4
5
|
import { isPromise } from "../utils/is-promise.mjs";
|
|
5
|
-
import { signJWT, symmetricDecodeJWT, symmetricEncodeJWT, verifyJWT } from "../crypto/jwt.mjs";
|
|
6
|
-
import { createAccountStore, createSessionStore, getAccountCookie, getChunkedCookie, setAccountCookie } from "./session-store.mjs";
|
|
7
6
|
import { sec } from "../utils/time.mjs";
|
|
8
7
|
import { HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, splitSetCookieHeader, stripSecureCookiePrefix } from "./cookie-utils.mjs";
|
|
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";
|
|
11
11
|
import { safeJSONParse } from "@better-auth/core/utils/json";
|
package/dist/crypto/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { constantTimeEqual } from "./buffer.mjs";
|
|
2
1
|
import { signJWT, symmetricDecodeJWT, symmetricEncodeJWT, verifyJWT } from "./jwt.mjs";
|
|
2
|
+
import { constantTimeEqual } from "./buffer.mjs";
|
|
3
3
|
import { hashPassword, verifyPassword } from "./password.mjs";
|
|
4
4
|
import { generateRandomString } from "./random.mjs";
|
|
5
|
-
import { createHash } from "@better-auth/utils/hash";
|
|
6
5
|
import { getWebcryptoSubtle } from "@better-auth/utils";
|
|
6
|
+
import { createHash } from "@better-auth/utils/hash";
|
|
7
7
|
import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
|
|
8
8
|
import { bytesToHex, hexToBytes, managedNonce, utf8ToBytes } from "@noble/ciphers/utils.js";
|
|
9
9
|
//#region src/crypto/index.ts
|
package/dist/db/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __exportAll, __reExport } from "../_virtual/_rolldown/runtime.mjs";
|
|
2
2
|
import { getSchema } from "./get-schema.mjs";
|
|
3
|
-
import { convertFromDB, convertToDB } from "./field-converter.mjs";
|
|
4
3
|
import { getSessionDefaultFields, mergeSchema, parseAccountInput, parseAccountOutput, parseAdditionalUserInput, parseInputData, parseSessionInput, parseSessionOutput, parseUserInput, parseUserOutput } from "./schema.mjs";
|
|
4
|
+
import { convertFromDB, convertToDB } from "./field-converter.mjs";
|
|
5
5
|
import { getWithHooks } from "./with-hooks.mjs";
|
|
6
6
|
import { createInternalAdapter } from "./internal-adapter.mjs";
|
|
7
7
|
import { toZodSchema } from "./to-zod.mjs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getIp } from "../utils/get-request-ip.mjs";
|
|
2
|
-
import { getDate } from "../utils/date.mjs";
|
|
3
2
|
import { getSessionDefaultFields, parseSessionOutput, parseUserOutput } from "./schema.mjs";
|
|
3
|
+
import { getDate } from "../utils/date.mjs";
|
|
4
4
|
import { getStorageOption, processIdentifier } from "./verification-token-storage.mjs";
|
|
5
5
|
import { getWithHooks } from "./with-hooks.mjs";
|
|
6
6
|
import { getCurrentAdapter, getCurrentAuthContext, runWithTransaction } from "@better-auth/core/context";
|
package/dist/index.d.mts
CHANGED
|
@@ -10,7 +10,7 @@ import { betterAuth } from "./auth/full.mjs";
|
|
|
10
10
|
import { generateState, parseState } from "./oauth2/state.mjs";
|
|
11
11
|
import { StateData, generateGenericState, parseGenericState } from "./state.mjs";
|
|
12
12
|
import { HIDE_METADATA } from "./utils/hide-metadata.mjs";
|
|
13
|
-
import { getBaseURL, getHost,
|
|
13
|
+
import { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL } from "./utils/url.mjs";
|
|
14
14
|
import { APIError } from "./api/index.mjs";
|
|
15
15
|
import { StandardSchemaV1 } from "@better-auth/core";
|
|
16
16
|
import { getCurrentAdapter } from "@better-auth/core/context";
|
|
@@ -27,4 +27,4 @@ export * from "@better-auth/core/utils/json";
|
|
|
27
27
|
export * from "@better-auth/core/social-providers";
|
|
28
28
|
export * from "better-call";
|
|
29
29
|
export * from "zod";
|
|
30
|
-
export { APIError, Account, AdditionalSessionFieldsInput, AdditionalUserFieldsInput, Auth, BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, BetterAuthCookies, BetterAuthOptions, BetterAuthPlugin, BetterAuthRateLimitOptions, ClientAtomListener, ClientStore, DBAdapter, DBAdapterInstance, DBAdapterSchemaCreation, DBTransactionAdapter, ExtractPluginField, FilteredAPI, HIDE_METADATA, HasRequiredKeys, InferAPI, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferOptionSchema, InferPluginContext, InferPluginErrorCodes, InferPluginFieldFromTuple, InferPluginIDs, InferPluginTypes, InferSessionAPI, InferSessionFromClient, InferUserFromClient, IsAny, IsSignal, type JSONWebKeySet, type JWTPayload, JoinConfig, JoinOption, OverrideMerge, Prettify, PrettifyDeep, RateLimit, RequiredKeysOf, Session, SessionQueryParams, type StandardSchemaV1, StateData, StoreIdentifierOption, StripEmptyObjects, type TelemetryEvent, UnionToIntersection, User, Verification, Where, betterAuth, createTelemetry, generateGenericState, generateState, getBaseURL, getCurrentAdapter, getHost,
|
|
30
|
+
export { APIError, Account, AdditionalSessionFieldsInput, AdditionalUserFieldsInput, Auth, BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, BetterAuthCookies, BetterAuthOptions, BetterAuthPlugin, BetterAuthRateLimitOptions, ClientAtomListener, ClientStore, DBAdapter, DBAdapterInstance, DBAdapterSchemaCreation, DBTransactionAdapter, ExtractPluginField, FilteredAPI, HIDE_METADATA, HasRequiredKeys, InferAPI, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferOptionSchema, InferPluginContext, InferPluginErrorCodes, InferPluginFieldFromTuple, InferPluginIDs, InferPluginTypes, InferSessionAPI, InferSessionFromClient, InferUserFromClient, IsAny, IsSignal, type JSONWebKeySet, type JWTPayload, JoinConfig, JoinOption, OverrideMerge, Prettify, PrettifyDeep, RateLimit, RequiredKeysOf, Session, SessionQueryParams, type StandardSchemaV1, StateData, StoreIdentifierOption, StripEmptyObjects, type TelemetryEvent, UnionToIntersection, User, Verification, Where, betterAuth, createTelemetry, generateGenericState, generateState, getBaseURL, getCurrentAdapter, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, getTelemetryAuthConfig, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, parseGenericState, parseState, resolveBaseURL, resolveDynamicBaseURL };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getBaseURL, getHost,
|
|
1
|
+
import { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL } from "./utils/url.mjs";
|
|
2
2
|
import { generateGenericState, parseGenericState } from "./state.mjs";
|
|
3
3
|
import { generateState, parseState } from "./oauth2/state.mjs";
|
|
4
4
|
import { HIDE_METADATA } from "./utils/hide-metadata.mjs";
|
|
@@ -14,4 +14,4 @@ export * from "@better-auth/core/oauth2";
|
|
|
14
14
|
export * from "@better-auth/core/utils/error-codes";
|
|
15
15
|
export * from "@better-auth/core/utils/id";
|
|
16
16
|
export * from "@better-auth/core/utils/json";
|
|
17
|
-
export { APIError, HIDE_METADATA, betterAuth, createTelemetry, generateGenericState, generateState, getBaseURL, getCurrentAdapter, getHost,
|
|
17
|
+
export { APIError, HIDE_METADATA, betterAuth, createTelemetry, generateGenericState, generateState, getBaseURL, getCurrentAdapter, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, getTelemetryAuthConfig, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, parseGenericState, parseState, resolveBaseURL, resolveDynamicBaseURL };
|
package/dist/package.mjs
CHANGED
|
@@ -775,11 +775,9 @@ declare const admin: <O extends AdminOptions>(options?: O | undefined) => {
|
|
|
775
775
|
body: zod.ZodIntersection<zod.ZodObject<{
|
|
776
776
|
userId: zod.ZodOptional<zod.ZodCoercedString<unknown>>;
|
|
777
777
|
role: zod.ZodOptional<zod.ZodString>;
|
|
778
|
-
}, zod_v4_core0.$strip>, zod.
|
|
778
|
+
}, zod_v4_core0.$strip>, zod.ZodXor<readonly [zod.ZodObject<{
|
|
779
779
|
permission: zod.ZodRecord<zod.ZodString, zod.ZodArray<zod.ZodString>>;
|
|
780
|
-
permissions: zod.ZodUndefined;
|
|
781
780
|
}, zod_v4_core0.$strip>, zod.ZodObject<{
|
|
782
|
-
permission: zod.ZodUndefined;
|
|
783
781
|
permissions: zod.ZodRecord<zod.ZodString, zod.ZodArray<zod.ZodString>>;
|
|
784
782
|
}, zod_v4_core0.$strip>]>>;
|
|
785
783
|
metadata: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getDate } from "../../utils/date.mjs";
|
|
2
1
|
import { parseSessionOutput, parseUserOutput } from "../../db/schema.mjs";
|
|
2
|
+
import { getDate } from "../../utils/date.mjs";
|
|
3
3
|
import { deleteSessionCookie, expireCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
4
4
|
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
5
5
|
import { ADMIN_ERROR_CODES } from "./error-codes.mjs";
|
|
@@ -766,13 +766,7 @@ const setUserPassword = (opts) => createAuthEndpoint("/admin/set-user-password",
|
|
|
766
766
|
const userHasPermissionBodySchema = z.object({
|
|
767
767
|
userId: z.coerce.string().optional().meta({ description: `The user id. Eg: "user-id"` }),
|
|
768
768
|
role: z.string().optional().meta({ description: `The role to check permission for. Eg: "admin"` })
|
|
769
|
-
}).and(z.
|
|
770
|
-
permission: z.record(z.string(), z.array(z.string())),
|
|
771
|
-
permissions: z.undefined()
|
|
772
|
-
}), z.object({
|
|
773
|
-
permission: z.undefined(),
|
|
774
|
-
permissions: z.record(z.string(), z.array(z.string()))
|
|
775
|
-
})]));
|
|
769
|
+
}).and(z.xor([z.object({ permission: z.record(z.string(), z.array(z.string())) }), z.object({ permissions: z.record(z.string(), z.array(z.string())) })]));
|
|
776
770
|
/**
|
|
777
771
|
* ### Endpoint
|
|
778
772
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { generateRandomString } from "../../crypto/random.mjs";
|
|
2
1
|
import { ms } from "../../utils/time.mjs";
|
|
2
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
3
|
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
4
4
|
import { DEVICE_AUTHORIZATION_ERROR_CODES } from "./error-codes.mjs";
|
|
5
5
|
import { APIError } from "@better-auth/core/error";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getDate } from "../../utils/date.mjs";
|
|
2
1
|
import { parseUserInput, parseUserOutput } from "../../db/schema.mjs";
|
|
2
|
+
import { getDate } from "../../utils/date.mjs";
|
|
3
3
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
4
4
|
import { symmetricDecrypt } from "../../crypto/index.mjs";
|
|
5
5
|
import { setCookieCache, setSessionCookie } from "../../cookies/index.mjs";
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -51,7 +51,7 @@ import { phoneNumber } from "./phone-number/index.mjs";
|
|
|
51
51
|
import { SIWEPluginOptions, siwe } from "./siwe/index.mjs";
|
|
52
52
|
import { LoginResult, TestCookie, TestHelpers, TestUtilsOptions } from "./test-utils/types.mjs";
|
|
53
53
|
import { testUtils } from "./test-utils/index.mjs";
|
|
54
|
-
import { BackupCodeOptions, backupCode2fa, generateBackupCodes, getBackupCodes, verifyBackupCode } from "./two-factor/backup-codes/index.mjs";
|
|
54
|
+
import { BackupCodeOptions, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, verifyBackupCode } from "./two-factor/backup-codes/index.mjs";
|
|
55
55
|
import { OTPOptions, otp2fa } from "./two-factor/otp/index.mjs";
|
|
56
56
|
import { TOTPOptions, totp2fa } from "./two-factor/totp/index.mjs";
|
|
57
57
|
import { TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor } from "./two-factor/types.mjs";
|
|
@@ -62,4 +62,4 @@ import { USERNAME_ERROR_CODES } from "./username/error-codes.mjs";
|
|
|
62
62
|
import { UsernameOptions, username } from "./username/index.mjs";
|
|
63
63
|
import { hasPermission } from "./organization/has-permission.mjs";
|
|
64
64
|
import { DefaultOrganizationPlugin, DynamicAccessControlEndpoints, OrganizationCreator, OrganizationEndpoints, OrganizationPlugin, TeamEndpoints, organization, parseRoles } from "./organization/organization.mjs";
|
|
65
|
-
export { AccessControl, AdminOptions, AnonymousOptions, AnonymousSession, ArrayElement, Auth0Options, AuthorizationQuery, AuthorizeResponse, BackupCodeOptions, BaseCaptchaOptions, BaseOAuthProviderOptions, BearerOptions, CaptchaFoxOptions, CaptchaOptions, Client, CloudflareTurnstileOptions, CodeVerificationValue, CustomSessionPluginOptions, DefaultOrganizationPlugin, DeviceAuthorizationOptions, DynamicAccessControlEndpoints, MULTI_SESSION_ERROR_CODES as ERROR_CODES, EmailOTPOptions, FieldSchema, GenericOAuthConfig, GenericOAuthOptions, GoogleRecaptchaOptions, GumroadOptions, HCaptchaOptions, HIDE_METADATA, HaveIBeenPwnedOptions, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOptionSchema, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginContext, InferPluginErrorCodes, InferPluginIDs, InferTeam, Invitation, InvitationInput, InvitationStatus, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodOptions, LineOptions, LoginResult, MagicLinkOptions, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OAuthProxyOptions, OIDCMetadata, OIDCOptions, OTPOptions, OktaOptions, OneTapOptions, OneTimeTokenOptions, OpenAPIModelSchema, OpenAPIOptions, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, Path, PatreonOptions, PhoneNumberOptions, Provider, Role, SIWEPluginOptions, SessionWithImpersonatedBy, SlackOptions, Statements, SubArray, Subset, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, TestCookie, TestHelpers, TestUtilsOptions, TimeString, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, UsernameOptions, admin, anonymous, auth0, backupCode2fa, bearer, captcha, createAccessControl, createJwk, customSession, defaultRolesSchema, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, generateBackupCodes, generateExportedKeyPair, generator, genericOAuth, getBackupCodes, getClient, getJwtToken, getMCPProtectedResourceMetadata, getMCPProviderMetadata, getMetadata, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, invitationSchema, invitationStatus, jwt, keycloak, lastLoginMethod, line, magicLink, mcp, memberSchema, microsoftEntraId, ms, multiSession, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, oAuthProxy, oidcProvider, okta, oneTap, oneTimeToken, openAPI, organization, organizationRoleSchema, organizationSchema, otp2fa, parseRoles, patreon, phoneNumber, role, roleSchema, sec, signJWT, siwe, slack, teamMemberSchema, teamSchema, testUtils, toExpJWT, totp2fa, twoFactor, twoFactorClient, username, verifyBackupCode, verifyJWT, withMcpAuth };
|
|
65
|
+
export { AccessControl, AdminOptions, AnonymousOptions, AnonymousSession, ArrayElement, Auth0Options, AuthorizationQuery, AuthorizeResponse, BackupCodeOptions, BaseCaptchaOptions, BaseOAuthProviderOptions, BearerOptions, CaptchaFoxOptions, CaptchaOptions, Client, CloudflareTurnstileOptions, CodeVerificationValue, CustomSessionPluginOptions, DefaultOrganizationPlugin, DeviceAuthorizationOptions, DynamicAccessControlEndpoints, MULTI_SESSION_ERROR_CODES as ERROR_CODES, EmailOTPOptions, FieldSchema, GenericOAuthConfig, GenericOAuthOptions, GoogleRecaptchaOptions, GumroadOptions, HCaptchaOptions, HIDE_METADATA, HaveIBeenPwnedOptions, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOptionSchema, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginContext, InferPluginErrorCodes, InferPluginIDs, InferTeam, Invitation, InvitationInput, InvitationStatus, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodOptions, LineOptions, LoginResult, MagicLinkOptions, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OAuthProxyOptions, OIDCMetadata, OIDCOptions, OTPOptions, OktaOptions, OneTapOptions, OneTimeTokenOptions, OpenAPIModelSchema, OpenAPIOptions, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, Path, PatreonOptions, PhoneNumberOptions, Provider, Role, SIWEPluginOptions, SessionWithImpersonatedBy, SlackOptions, Statements, SubArray, Subset, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, TestCookie, TestHelpers, TestUtilsOptions, TimeString, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, UsernameOptions, admin, anonymous, auth0, backupCode2fa, bearer, captcha, createAccessControl, createJwk, customSession, defaultRolesSchema, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, encodeBackupCodes, generateBackupCodes, generateExportedKeyPair, generator, genericOAuth, getBackupCodes, getClient, getJwtToken, getMCPProtectedResourceMetadata, getMCPProviderMetadata, getMetadata, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, invitationSchema, invitationStatus, jwt, keycloak, lastLoginMethod, line, magicLink, mcp, memberSchema, microsoftEntraId, ms, multiSession, oAuthDiscoveryMetadata, oAuthProtectedResourceMetadata, oAuthProxy, oidcProvider, okta, oneTap, oneTimeToken, openAPI, organization, organizationRoleSchema, organizationSchema, otp2fa, parseRoles, patreon, phoneNumber, role, roleSchema, sec, signJWT, siwe, slack, teamMemberSchema, teamSchema, testUtils, toExpJWT, totp2fa, twoFactor, twoFactorClient, username, verifyBackupCode, verifyJWT, withMcpAuth };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { symmetricEncrypt } from "../../crypto/index.mjs";
|
|
2
1
|
import { sec } from "../../utils/time.mjs";
|
|
2
|
+
import { symmetricEncrypt } from "../../crypto/index.mjs";
|
|
3
3
|
import { getJwksAdapter } from "./adapter.mjs";
|
|
4
4
|
import { exportJWK, generateKeyPair } from "jose";
|
|
5
5
|
//#region src/plugins/jwt/utils.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { getBaseURL, isDynamicBaseURLConfig, resolveBaseURL } from "../../utils/url.mjs";
|
|
2
|
-
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
2
|
import { parseSetCookieHeader } from "../../cookies/cookie-utils.mjs";
|
|
3
|
+
import { generateRandomString } from "../../crypto/random.mjs";
|
|
4
4
|
import { expireCookie } from "../../cookies/index.mjs";
|
|
5
|
+
import { resolveDynamicTrustedProxyHeaders } from "../../context/helpers.mjs";
|
|
5
6
|
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
6
7
|
import { HIDE_METADATA } from "../../utils/hide-metadata.mjs";
|
|
7
8
|
import { APIError } from "../../api/index.mjs";
|
|
@@ -15,9 +16,9 @@ import { safeJSONParse } from "@better-auth/core/utils/json";
|
|
|
15
16
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
16
17
|
import * as z from "zod";
|
|
17
18
|
import { base64 } from "@better-auth/utils/base64";
|
|
18
|
-
import { createHash } from "@better-auth/utils/hash";
|
|
19
|
-
import { getWebcryptoSubtle } from "@better-auth/utils";
|
|
20
19
|
import { SignJWT } from "jose";
|
|
20
|
+
import { getWebcryptoSubtle } from "@better-auth/utils";
|
|
21
|
+
import { createHash } from "@better-auth/utils/hash";
|
|
21
22
|
//#region src/plugins/mcp/index.ts
|
|
22
23
|
const getMCPProviderMetadata = (ctx, options) => {
|
|
23
24
|
const issuer = typeof ctx.context.options.baseURL === "string" ? ctx.context.options.baseURL : "";
|
|
@@ -662,10 +663,15 @@ const mcp = (options) => {
|
|
|
662
663
|
const withMcpAuth = (auth, handler) => {
|
|
663
664
|
return async (req) => {
|
|
664
665
|
const basePath = auth.options.basePath || "/api/auth";
|
|
665
|
-
const
|
|
666
|
+
const trustedProxyHeaders = resolveDynamicTrustedProxyHeaders(auth.options);
|
|
667
|
+
const baseURL = isDynamicBaseURLConfig(auth.options.baseURL) ? resolveBaseURL(auth.options.baseURL, basePath, req, void 0, trustedProxyHeaders) : getBaseURL(typeof auth.options.baseURL === "string" ? auth.options.baseURL : void 0, basePath);
|
|
666
668
|
if (!baseURL && !isProduction) logger.warn("Unable to get the baseURL, please check your config!");
|
|
667
|
-
const session = await auth.api.getMcpSession({
|
|
668
|
-
|
|
669
|
+
const session = await auth.api.getMcpSession({
|
|
670
|
+
request: req,
|
|
671
|
+
headers: req.headers,
|
|
672
|
+
asResponse: false
|
|
673
|
+
});
|
|
674
|
+
const wwwAuthenticateValue = baseURL ? `Bearer resource_metadata="${baseURL}/.well-known/oauth-protected-resource"` : "Bearer";
|
|
669
675
|
if (!session) return Response.json({
|
|
670
676
|
jsonrpc: "2.0",
|
|
671
677
|
error: {
|
|
@@ -686,7 +692,10 @@ const withMcpAuth = (auth, handler) => {
|
|
|
686
692
|
};
|
|
687
693
|
const oAuthDiscoveryMetadata = (auth) => {
|
|
688
694
|
return async (request) => {
|
|
689
|
-
const res = await auth.api.getMcpOAuthConfig(
|
|
695
|
+
const res = await auth.api.getMcpOAuthConfig({
|
|
696
|
+
request,
|
|
697
|
+
asResponse: false
|
|
698
|
+
});
|
|
690
699
|
return new Response(JSON.stringify(res), {
|
|
691
700
|
status: 200,
|
|
692
701
|
headers: {
|
|
@@ -701,7 +710,10 @@ const oAuthDiscoveryMetadata = (auth) => {
|
|
|
701
710
|
};
|
|
702
711
|
const oAuthProtectedResourceMetadata = (auth) => {
|
|
703
712
|
return async (request) => {
|
|
704
|
-
const res = await auth.api.getMCPProtectedResource(
|
|
713
|
+
const res = await auth.api.getMCPProtectedResource({
|
|
714
|
+
request,
|
|
715
|
+
asResponse: false
|
|
716
|
+
});
|
|
705
717
|
return new Response(JSON.stringify(res), {
|
|
706
718
|
status: 200,
|
|
707
719
|
headers: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getOrigin } from "../../utils/url.mjs";
|
|
2
2
|
import { originCheck } from "../../api/middlewares/origin-check.mjs";
|
|
3
|
-
import { symmetricDecrypt, symmetricEncrypt } from "../../crypto/index.mjs";
|
|
4
3
|
import { parseSetCookieHeader } from "../../cookies/cookie-utils.mjs";
|
|
4
|
+
import { symmetricDecrypt, symmetricEncrypt } from "../../crypto/index.mjs";
|
|
5
5
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
6
6
|
import { parseGenericState } from "../../state.mjs";
|
|
7
7
|
import { handleOAuthUserInfo } from "../../oauth2/link-account.mjs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mergeSchema } from "../../db/schema.mjs";
|
|
2
|
+
import { parseSetCookieHeader } from "../../cookies/cookie-utils.mjs";
|
|
2
3
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
4
|
import { symmetricDecrypt, symmetricEncrypt } from "../../crypto/index.mjs";
|
|
4
|
-
import { parseSetCookieHeader } from "../../cookies/cookie-utils.mjs";
|
|
5
5
|
import { expireCookie } from "../../cookies/index.mjs";
|
|
6
6
|
import { getSessionFromCtx, sessionMiddleware } from "../../api/routes/session.mjs";
|
|
7
7
|
import { HIDE_METADATA } from "../../utils/hide-metadata.mjs";
|
|
@@ -18,8 +18,8 @@ import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"
|
|
|
18
18
|
import { deprecate } from "@better-auth/core/utils/deprecate";
|
|
19
19
|
import * as z from "zod";
|
|
20
20
|
import { base64 } from "@better-auth/utils/base64";
|
|
21
|
-
import { createHash } from "@better-auth/utils/hash";
|
|
22
21
|
import { SignJWT, jwtVerify } from "jose";
|
|
22
|
+
import { createHash } from "@better-auth/utils/hash";
|
|
23
23
|
//#region src/plugins/oidc-provider/index.ts
|
|
24
24
|
/**
|
|
25
25
|
* Get a client by ID, checking trusted clients first, then database
|
|
@@ -99,11 +99,9 @@ declare const createHasPermission: <O extends OrganizationOptions>(options: O) =
|
|
|
99
99
|
requireHeaders: true;
|
|
100
100
|
body: z.ZodIntersection<z.ZodObject<{
|
|
101
101
|
organizationId: z.ZodOptional<z.ZodString>;
|
|
102
|
-
}, z.core.$strip>, z.
|
|
102
|
+
}, z.core.$strip>, z.ZodXor<readonly [z.ZodObject<{
|
|
103
103
|
permission: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
|
|
104
|
-
permissions: z.ZodUndefined;
|
|
105
104
|
}, z.core.$strip>, z.ZodObject<{
|
|
106
|
-
permission: z.ZodUndefined;
|
|
107
105
|
permissions: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
|
|
108
106
|
}, z.core.$strip>]>>;
|
|
109
107
|
use: ((inputContext: better_call0.MiddlewareInputContext<{
|
|
@@ -18,13 +18,7 @@ import * as z from "zod";
|
|
|
18
18
|
function parseRoles(roles) {
|
|
19
19
|
return Array.isArray(roles) ? roles.join(",") : roles;
|
|
20
20
|
}
|
|
21
|
-
const createHasPermissionBodySchema = z.object({ organizationId: z.string().optional() }).and(z.
|
|
22
|
-
permission: z.record(z.string(), z.array(z.string())),
|
|
23
|
-
permissions: z.undefined()
|
|
24
|
-
}), z.object({
|
|
25
|
-
permission: z.undefined(),
|
|
26
|
-
permissions: z.record(z.string(), z.array(z.string()))
|
|
27
|
-
})]));
|
|
21
|
+
const createHasPermissionBodySchema = z.object({ organizationId: z.string().optional() }).and(z.xor([z.object({ permission: z.record(z.string(), z.array(z.string())) }), z.object({ permissions: z.record(z.string(), z.array(z.string())) })]));
|
|
28
22
|
const createHasPermission = (options) => {
|
|
29
23
|
return createAuthEndpoint("/organization/has-permission", {
|
|
30
24
|
method: "POST",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getDate } from "../../../utils/date.mjs";
|
|
2
|
-
import { toZodSchema } from "../../../db/to-zod.mjs";
|
|
3
2
|
import { setSessionCookie } from "../../../cookies/index.mjs";
|
|
3
|
+
import { toZodSchema } from "../../../db/to-zod.mjs";
|
|
4
4
|
import { getSessionFromCtx } from "../../../api/routes/session.mjs";
|
|
5
5
|
import { defaultRoles } from "../access/statement.mjs";
|
|
6
6
|
import { ORGANIZATION_ERROR_CODES } from "../error-codes.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { toZodSchema } from "../../../db/to-zod.mjs";
|
|
2
1
|
import { setSessionCookie } from "../../../cookies/index.mjs";
|
|
2
|
+
import { toZodSchema } from "../../../db/to-zod.mjs";
|
|
3
3
|
import { getSessionFromCtx, requestOnlySessionMiddleware } from "../../../api/routes/session.mjs";
|
|
4
4
|
import { ORGANIZATION_ERROR_CODES } from "../error-codes.mjs";
|
|
5
5
|
import { getOrgAdapter } from "../adapter.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { toZodSchema } from "../../../db/to-zod.mjs";
|
|
2
1
|
import { setSessionCookie } from "../../../cookies/index.mjs";
|
|
2
|
+
import { toZodSchema } from "../../../db/to-zod.mjs";
|
|
3
3
|
import { getSessionFromCtx } from "../../../api/routes/session.mjs";
|
|
4
4
|
import { ORGANIZATION_ERROR_CODES } from "../error-codes.mjs";
|
|
5
5
|
import { getOrgAdapter } from "../adapter.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getDate } from "../../utils/date.mjs";
|
|
2
1
|
import { parseUserInput, parseUserOutput } from "../../db/schema.mjs";
|
|
2
|
+
import { getDate } from "../../utils/date.mjs";
|
|
3
3
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
4
4
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
5
5
|
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
@@ -36,6 +36,7 @@ interface BackupCodeOptions {
|
|
|
36
36
|
*/
|
|
37
37
|
allowPasswordless?: boolean | undefined;
|
|
38
38
|
}
|
|
39
|
+
declare function encodeBackupCodes(codes: string[], secret: string | SecretConfig, options?: BackupCodeOptions | undefined): Promise<string>;
|
|
39
40
|
declare function generateBackupCodes(secret: string | SecretConfig, options?: BackupCodeOptions | undefined): Promise<{
|
|
40
41
|
backupCodes: string[];
|
|
41
42
|
encryptedBackupCodes: string;
|
|
@@ -286,4 +287,4 @@ declare const backupCode2fa: (opts: BackupCodeOptions) => {
|
|
|
286
287
|
};
|
|
287
288
|
};
|
|
288
289
|
//#endregion
|
|
289
|
-
export { BackupCodeOptions, backupCode2fa, generateBackupCodes, getBackupCodes, verifyBackupCode };
|
|
290
|
+
export { BackupCodeOptions, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, verifyBackupCode };
|
|
@@ -14,22 +14,20 @@ import * as z from "zod";
|
|
|
14
14
|
function generateBackupCodesFn(options) {
|
|
15
15
|
return Array.from({ length: options?.amount ?? 10 }).fill(null).map(() => generateRandomString(options?.length ?? 10, "a-z", "0-9", "A-Z")).map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
|
|
16
16
|
}
|
|
17
|
+
async function encodeBackupCodes(codes, secret, options) {
|
|
18
|
+
const json = JSON.stringify(codes);
|
|
19
|
+
if (options?.storeBackupCodes === "encrypted") return symmetricEncrypt({
|
|
20
|
+
data: json,
|
|
21
|
+
key: secret
|
|
22
|
+
});
|
|
23
|
+
if (typeof options?.storeBackupCodes === "object" && "encrypt" in options?.storeBackupCodes) return options.storeBackupCodes.encrypt(json);
|
|
24
|
+
return json;
|
|
25
|
+
}
|
|
17
26
|
async function generateBackupCodes(secret, options) {
|
|
18
27
|
const backupCodes = options?.customBackupCodesGenerate ? options.customBackupCodesGenerate() : generateBackupCodesFn(options);
|
|
19
|
-
if (options?.storeBackupCodes === "encrypted") return {
|
|
20
|
-
backupCodes,
|
|
21
|
-
encryptedBackupCodes: await symmetricEncrypt({
|
|
22
|
-
data: JSON.stringify(backupCodes),
|
|
23
|
-
key: secret
|
|
24
|
-
})
|
|
25
|
-
};
|
|
26
|
-
if (typeof options?.storeBackupCodes === "object" && "encrypt" in options?.storeBackupCodes) return {
|
|
27
|
-
backupCodes,
|
|
28
|
-
encryptedBackupCodes: await options?.storeBackupCodes.encrypt(JSON.stringify(backupCodes))
|
|
29
|
-
};
|
|
30
28
|
return {
|
|
31
29
|
backupCodes,
|
|
32
|
-
encryptedBackupCodes:
|
|
30
|
+
encryptedBackupCodes: await encodeBackupCodes(backupCodes, secret, options)
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
33
|
async function verifyBackupCode(data, key, options) {
|
|
@@ -177,11 +175,8 @@ const backupCode2fa = (opts) => {
|
|
|
177
175
|
backupCodes: twoFactor.backupCodes,
|
|
178
176
|
code: ctx.body.code
|
|
179
177
|
}, ctx.context.secretConfig, opts);
|
|
180
|
-
if (!validate.status) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE);
|
|
181
|
-
const updatedBackupCodes = await
|
|
182
|
-
key: ctx.context.secretConfig,
|
|
183
|
-
data: JSON.stringify(validate.updated)
|
|
184
|
-
});
|
|
178
|
+
if (!validate.status || !validate.updated) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE);
|
|
179
|
+
const updatedBackupCodes = await encodeBackupCodes(validate.updated, ctx.context.secretConfig, opts);
|
|
185
180
|
if (!await ctx.context.adapter.update({
|
|
186
181
|
model: twoFactorTable,
|
|
187
182
|
update: { backupCodes: updatedBackupCodes },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BackupCodeOptions, backupCode2fa, generateBackupCodes, getBackupCodes, verifyBackupCode } from "./backup-codes/index.mjs";
|
|
1
|
+
import { BackupCodeOptions, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, verifyBackupCode } from "./backup-codes/index.mjs";
|
|
2
2
|
import { OTPOptions, otp2fa } from "./otp/index.mjs";
|
|
3
3
|
import { TOTPOptions, totp2fa } from "./totp/index.mjs";
|
|
4
4
|
import { TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor } from "./types.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BackupCodeOptions, backupCode2fa, generateBackupCodes, getBackupCodes, verifyBackupCode } from "./backup-codes/index.mjs";
|
|
1
|
+
import { BackupCodeOptions, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, verifyBackupCode } from "./backup-codes/index.mjs";
|
|
2
2
|
import { OTPOptions, otp2fa } from "./otp/index.mjs";
|
|
3
3
|
import { TOTPOptions, totp2fa } from "./totp/index.mjs";
|
|
4
4
|
import { TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor } from "./types.mjs";
|
|
@@ -683,4 +683,4 @@ declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
683
683
|
};
|
|
684
684
|
};
|
|
685
685
|
//#endregion
|
|
686
|
-
export { BackupCodeOptions, OTPOptions, TOTPOptions, TWO_FACTOR_ERROR_CODES, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor, backupCode2fa, generateBackupCodes, getBackupCodes, otp2fa, totp2fa, twoFactor, twoFactorClient, verifyBackupCode };
|
|
686
|
+
export { BackupCodeOptions, OTPOptions, TOTPOptions, TWO_FACTOR_ERROR_CODES, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, otp2fa, totp2fa, twoFactor, twoFactorClient, verifyBackupCode };
|
|
@@ -261,7 +261,6 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
261
261
|
"application/json": {
|
|
262
262
|
schema: {
|
|
263
263
|
type: "object";
|
|
264
|
-
nullable: boolean;
|
|
265
264
|
properties: {
|
|
266
265
|
session: {
|
|
267
266
|
$ref: string;
|
|
@@ -2265,7 +2264,6 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
2265
2264
|
"application/json": {
|
|
2266
2265
|
schema: {
|
|
2267
2266
|
type: "object";
|
|
2268
|
-
nullable: boolean;
|
|
2269
2267
|
properties: {
|
|
2270
2268
|
session: {
|
|
2271
2269
|
$ref: string;
|
|
@@ -4272,7 +4270,6 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
4272
4270
|
"application/json": {
|
|
4273
4271
|
schema: {
|
|
4274
4272
|
type: "object";
|
|
4275
|
-
nullable: boolean;
|
|
4276
4273
|
properties: {
|
|
4277
4274
|
session: {
|
|
4278
4275
|
$ref: string;
|
|
@@ -6276,7 +6273,6 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
6276
6273
|
"application/json": {
|
|
6277
6274
|
schema: {
|
|
6278
6275
|
type: "object";
|
|
6279
|
-
nullable: boolean;
|
|
6280
6276
|
properties: {
|
|
6281
6277
|
session: {
|
|
6282
6278
|
$ref: string;
|
|
@@ -8354,7 +8350,6 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
8354
8350
|
"application/json": {
|
|
8355
8351
|
schema: {
|
|
8356
8352
|
type: "object";
|
|
8357
|
-
nullable: boolean;
|
|
8358
8353
|
properties: {
|
|
8359
8354
|
session: {
|
|
8360
8355
|
$ref: string;
|
|
@@ -10358,7 +10353,6 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
10358
10353
|
"application/json": {
|
|
10359
10354
|
schema: {
|
|
10360
10355
|
type: "object";
|
|
10361
|
-
nullable: boolean;
|
|
10362
10356
|
properties: {
|
|
10363
10357
|
session: {
|
|
10364
10358
|
$ref: string;
|
|
@@ -80,7 +80,13 @@ async function getTestInstance(options, config) {
|
|
|
80
80
|
};
|
|
81
81
|
async function createTestUser() {
|
|
82
82
|
if (config?.disableTestUser) return;
|
|
83
|
-
|
|
83
|
+
const dynamicBaseURL = isDynamicBaseURLConfig(auth.options.baseURL) ? auth.options.baseURL : void 0;
|
|
84
|
+
const host = (dynamicBaseURL?.allowedHosts.find((h) => !h.includes("*") && !h.includes("?")) ?? dynamicBaseURL?.allowedHosts[0])?.replace(/^https?:\/\//, "").split(/[/#]/)[0]?.replace(/\*/g, "test").replace(/\?/g, "x");
|
|
85
|
+
const headers = host ? new Headers({ host }) : void 0;
|
|
86
|
+
await auth.api.signUpEmail({
|
|
87
|
+
body: testUser,
|
|
88
|
+
headers
|
|
89
|
+
});
|
|
84
90
|
}
|
|
85
91
|
if (testWith !== "mongodb") {
|
|
86
92
|
const { runMigrations } = await getMigrations({
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { generateState, parseState } from "../oauth2/state.mjs";
|
|
2
2
|
import { StateData, generateGenericState, parseGenericState } from "../state.mjs";
|
|
3
3
|
import { HIDE_METADATA } from "./hide-metadata.mjs";
|
|
4
|
-
import { getBaseURL, getHost,
|
|
4
|
+
import { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL } from "./url.mjs";
|
package/dist/utils/url.d.mts
CHANGED
|
@@ -10,22 +10,29 @@ declare function getHost(url: string): string | null;
|
|
|
10
10
|
*/
|
|
11
11
|
declare function isDynamicBaseURLConfig(config: BaseURLConfig | undefined): config is DynamicBaseURLConfig;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Check if a value is a `Request`
|
|
14
|
+
* - `instanceof`: works for native Request instances
|
|
15
|
+
* - `toString`: handles where instanceof check fails but the object is still a
|
|
16
|
+
* valid Request (e.g. cross-realm, polyfills). Paired with a shape check so
|
|
17
|
+
* an object that only spoofs `Symbol.toStringTag` without the real shape is
|
|
18
|
+
* rejected before downstream code tries to read `.headers` / `.url`.
|
|
15
19
|
*
|
|
16
|
-
* @param
|
|
17
|
-
* @returns
|
|
20
|
+
* @param value The value to check
|
|
21
|
+
* @returns `true` if the value is a Request instance
|
|
18
22
|
*/
|
|
19
|
-
declare function
|
|
23
|
+
declare function isRequestLike(value: unknown): value is Request;
|
|
20
24
|
/**
|
|
21
|
-
* Extracts the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
* Extracts the host from a `Request` or `Headers`.
|
|
26
|
+
* Honors `x-forwarded-host` only when `trustedProxyHeaders` is enabled,
|
|
27
|
+
* then falls back to the `host` header and finally the request URL.
|
|
28
|
+
*/
|
|
29
|
+
declare function getHostFromSource(source: Request | Headers, trustedProxyHeaders?: boolean): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Extracts the protocol from a `Request` or `Headers`.
|
|
32
|
+
* Honors `x-forwarded-proto` only when `trustedProxyHeaders` is enabled,
|
|
33
|
+
* then falls back to the request URL, then to "https".
|
|
27
34
|
*/
|
|
28
|
-
declare function
|
|
35
|
+
declare function getProtocolFromSource(source: Request | Headers, configProtocol?: "http" | "https" | "auto" | undefined, trustedProxyHeaders?: boolean): "http" | "https";
|
|
29
36
|
/**
|
|
30
37
|
* Matches a hostname against a host pattern.
|
|
31
38
|
* Supports wildcard patterns like `*.vercel.app` or `preview-*.myapp.com`.
|
|
@@ -53,7 +60,7 @@ declare const matchesHostPattern: (host: string, pattern: string) => boolean;
|
|
|
53
60
|
* @returns The resolved base URL with path
|
|
54
61
|
* @throws BetterAuthError if host is not in allowedHosts and no fallback is set
|
|
55
62
|
*/
|
|
56
|
-
declare function resolveDynamicBaseURL(config: DynamicBaseURLConfig,
|
|
63
|
+
declare function resolveDynamicBaseURL(config: DynamicBaseURLConfig, source: Request | Headers, basePath: string, trustedProxyHeaders?: boolean): string;
|
|
57
64
|
/**
|
|
58
65
|
* Resolves the base URL from any config type (static string or dynamic object).
|
|
59
66
|
* This is the main entry point for base URL resolution.
|
|
@@ -65,6 +72,6 @@ declare function resolveDynamicBaseURL(config: DynamicBaseURLConfig, request: Re
|
|
|
65
72
|
* @param trustedProxyHeaders Whether to trust proxy headers (for legacy behavior)
|
|
66
73
|
* @returns The resolved base URL with path
|
|
67
74
|
*/
|
|
68
|
-
declare function resolveBaseURL(config: BaseURLConfig | undefined, basePath: string,
|
|
75
|
+
declare function resolveBaseURL(config: BaseURLConfig | undefined, basePath: string, source?: Request | Headers, loadEnv?: boolean, trustedProxyHeaders?: boolean): string | undefined;
|
|
69
76
|
//#endregion
|
|
70
|
-
export { getBaseURL, getHost,
|
|
77
|
+
export { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL };
|
package/dist/utils/url.mjs
CHANGED
|
@@ -93,41 +93,66 @@ function isDynamicBaseURLConfig(config) {
|
|
|
93
93
|
return typeof config === "object" && config !== null && "allowedHosts" in config && Array.isArray(config.allowedHosts);
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
96
|
+
* Check if a value is a `Request`
|
|
97
|
+
* - `instanceof`: works for native Request instances
|
|
98
|
+
* - `toString`: handles where instanceof check fails but the object is still a
|
|
99
|
+
* valid Request (e.g. cross-realm, polyfills). Paired with a shape check so
|
|
100
|
+
* an object that only spoofs `Symbol.toStringTag` without the real shape is
|
|
101
|
+
* rejected before downstream code tries to read `.headers` / `.url`.
|
|
98
102
|
*
|
|
99
|
-
* @param
|
|
100
|
-
* @returns
|
|
103
|
+
* @param value The value to check
|
|
104
|
+
* @returns `true` if the value is a Request instance
|
|
105
|
+
*/
|
|
106
|
+
function isRequestLike(value) {
|
|
107
|
+
if (value instanceof Request) return true;
|
|
108
|
+
if (typeof value !== "object" || value === null || Object.prototype.toString.call(value) !== "[object Request]") return false;
|
|
109
|
+
const v = value;
|
|
110
|
+
return typeof v.url === "string" && typeof v.headers === "object" && v.headers !== null && typeof v.headers.get === "function";
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Extracts the host from a `Request` or `Headers`.
|
|
114
|
+
* Honors `x-forwarded-host` only when `trustedProxyHeaders` is enabled,
|
|
115
|
+
* then falls back to the `host` header and finally the request URL.
|
|
101
116
|
*/
|
|
102
|
-
function
|
|
103
|
-
const
|
|
104
|
-
if (
|
|
105
|
-
|
|
117
|
+
function getHostFromSource(source, trustedProxyHeaders) {
|
|
118
|
+
const headers = isRequestLike(source) ? source.headers : source;
|
|
119
|
+
if (trustedProxyHeaders) {
|
|
120
|
+
const forwardedHost = headers.get("x-forwarded-host");
|
|
121
|
+
if (forwardedHost && validateProxyHeader(forwardedHost, "host")) return forwardedHost;
|
|
122
|
+
}
|
|
123
|
+
const host = headers.get("host");
|
|
106
124
|
if (host && validateProxyHeader(host, "host")) return host;
|
|
107
|
-
try {
|
|
108
|
-
return new URL(
|
|
125
|
+
if (isRequestLike(source)) try {
|
|
126
|
+
return new URL(source.url).host;
|
|
109
127
|
} catch {
|
|
110
128
|
return null;
|
|
111
129
|
}
|
|
130
|
+
return null;
|
|
112
131
|
}
|
|
113
132
|
/**
|
|
114
|
-
* Extracts the protocol from
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @param request The incoming request
|
|
118
|
-
* @param configProtocol Protocol override from config
|
|
119
|
-
* @returns The protocol ("http" or "https")
|
|
133
|
+
* Extracts the protocol from a `Request` or `Headers`.
|
|
134
|
+
* Honors `x-forwarded-proto` only when `trustedProxyHeaders` is enabled,
|
|
135
|
+
* then falls back to the request URL, then to "https".
|
|
120
136
|
*/
|
|
121
|
-
function
|
|
137
|
+
function getProtocolFromSource(source, configProtocol, trustedProxyHeaders) {
|
|
122
138
|
if (configProtocol === "http" || configProtocol === "https") return configProtocol;
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
139
|
+
const headers = isRequestLike(source) ? source.headers : source;
|
|
140
|
+
if (trustedProxyHeaders) {
|
|
141
|
+
const forwardedProto = headers.get("x-forwarded-proto");
|
|
142
|
+
if (forwardedProto && validateProxyHeader(forwardedProto, "proto")) return forwardedProto;
|
|
143
|
+
}
|
|
144
|
+
if (isRequestLike(source)) try {
|
|
145
|
+
const url = new URL(source.url);
|
|
127
146
|
if (url.protocol === "http:" || url.protocol === "https:") return url.protocol.slice(0, -1);
|
|
128
147
|
} catch {}
|
|
148
|
+
const host = getHostFromSource(source, trustedProxyHeaders);
|
|
149
|
+
if (host && isLoopbackHost(host)) return "http";
|
|
129
150
|
return "https";
|
|
130
151
|
}
|
|
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
|
+
}
|
|
131
156
|
/**
|
|
132
157
|
* Matches a hostname against a host pattern.
|
|
133
158
|
* Supports wildcard patterns like `*.vercel.app` or `preview-*.myapp.com`.
|
|
@@ -161,13 +186,13 @@ const matchesHostPattern = (host, pattern) => {
|
|
|
161
186
|
* @returns The resolved base URL with path
|
|
162
187
|
* @throws BetterAuthError if host is not in allowedHosts and no fallback is set
|
|
163
188
|
*/
|
|
164
|
-
function resolveDynamicBaseURL(config,
|
|
165
|
-
const host =
|
|
189
|
+
function resolveDynamicBaseURL(config, source, basePath, trustedProxyHeaders) {
|
|
190
|
+
const host = getHostFromSource(source, trustedProxyHeaders);
|
|
166
191
|
if (!host) {
|
|
167
192
|
if (config.fallback) return withPath(config.fallback, basePath);
|
|
168
193
|
throw new BetterAuthError("Could not determine host from request headers. Please provide a fallback URL in your baseURL config.");
|
|
169
194
|
}
|
|
170
|
-
if (config.allowedHosts.some((pattern) => matchesHostPattern(host, pattern))) return withPath(`${
|
|
195
|
+
if (config.allowedHosts.some((pattern) => matchesHostPattern(host, pattern))) return withPath(`${getProtocolFromSource(source, config.protocol, trustedProxyHeaders)}://${host}`, basePath);
|
|
171
196
|
if (config.fallback) return withPath(config.fallback, basePath);
|
|
172
197
|
throw new BetterAuthError(`Host "${host}" is not in the allowed hosts list. Allowed hosts: ${config.allowedHosts.join(", ")}. Add this host to your allowedHosts config or provide a fallback URL.`);
|
|
173
198
|
}
|
|
@@ -182,14 +207,15 @@ function resolveDynamicBaseURL(config, request, basePath) {
|
|
|
182
207
|
* @param trustedProxyHeaders Whether to trust proxy headers (for legacy behavior)
|
|
183
208
|
* @returns The resolved base URL with path
|
|
184
209
|
*/
|
|
185
|
-
function resolveBaseURL(config, basePath,
|
|
210
|
+
function resolveBaseURL(config, basePath, source, loadEnv, trustedProxyHeaders) {
|
|
186
211
|
if (isDynamicBaseURLConfig(config)) {
|
|
187
|
-
if (
|
|
212
|
+
if (source) return resolveDynamicBaseURL(config, source, basePath, trustedProxyHeaders);
|
|
188
213
|
if (config.fallback) return withPath(config.fallback, basePath);
|
|
189
|
-
return getBaseURL(void 0, basePath,
|
|
214
|
+
return getBaseURL(void 0, basePath, void 0, loadEnv, trustedProxyHeaders);
|
|
190
215
|
}
|
|
216
|
+
const request = isRequestLike(source) ? source : void 0;
|
|
191
217
|
if (typeof config === "string") return getBaseURL(config, basePath, request, loadEnv, trustedProxyHeaders);
|
|
192
218
|
return getBaseURL(void 0, basePath, request, loadEnv, trustedProxyHeaders);
|
|
193
219
|
}
|
|
194
220
|
//#endregion
|
|
195
|
-
export { getBaseURL, getHost,
|
|
221
|
+
export { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -489,17 +489,17 @@
|
|
|
489
489
|
"kysely": "^0.28.14",
|
|
490
490
|
"nanostores": "^1.1.1",
|
|
491
491
|
"zod": "^4.3.6",
|
|
492
|
-
"@better-auth/core": "1.6.
|
|
493
|
-
"@better-auth/drizzle-adapter": "1.6.
|
|
494
|
-
"@better-auth/kysely-adapter": "1.6.
|
|
495
|
-
"@better-auth/memory-adapter": "1.6.
|
|
496
|
-
"@better-auth/mongo-adapter": "1.6.
|
|
497
|
-
"@better-auth/prisma-adapter": "1.6.
|
|
498
|
-
"@better-auth/telemetry": "1.6.
|
|
492
|
+
"@better-auth/core": "1.6.4",
|
|
493
|
+
"@better-auth/drizzle-adapter": "1.6.4",
|
|
494
|
+
"@better-auth/kysely-adapter": "1.6.4",
|
|
495
|
+
"@better-auth/memory-adapter": "1.6.4",
|
|
496
|
+
"@better-auth/mongo-adapter": "1.6.4",
|
|
497
|
+
"@better-auth/prisma-adapter": "1.6.4",
|
|
498
|
+
"@better-auth/telemetry": "1.6.4"
|
|
499
499
|
},
|
|
500
500
|
"devDependencies": {
|
|
501
501
|
"@lynx-js/react": "^0.116.3",
|
|
502
|
-
"@sveltejs/kit": "^2.
|
|
502
|
+
"@sveltejs/kit": "^2.57.1",
|
|
503
503
|
"@tanstack/react-start": "^1.163.2",
|
|
504
504
|
"@tanstack/solid-start": "^1.163.2",
|
|
505
505
|
"@types/bun": "^1.3.9",
|
|
@@ -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",
|