better-auth 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { Prettify as Prettify$1, UnionToIntersection } from "../types/helper.mjs";
1
+ import { OverrideMerge, Prettify as Prettify$1, UnionToIntersection } from "../types/helper.mjs";
2
2
  import { AdditionalSessionFieldsInput, AdditionalUserFieldsInput } from "../types/models.mjs";
3
3
  import { getIp } from "../utils/get-request-ip.mjs";
4
4
  import { isAPIError } from "../utils/is-api-error.mjs";
@@ -31,7 +31,7 @@ import * as zod_v4_core0 from "zod/v4/core";
31
31
  //#region src/api/index.d.ts
32
32
  declare function checkEndpointConflicts(options: BetterAuthOptions, logger: InternalLogger): void;
33
33
  declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<AuthContext>, options: Option): {
34
- api: Omit<{
34
+ api: OverrideMerge<{
35
35
  readonly ok: better_call0.StrictEndpoint<"/ok", {
36
36
  method: "GET";
37
37
  metadata: {
@@ -2007,11 +2007,9 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
2007
2007
  user: _better_auth_core_oauth20.OAuth2UserInfo;
2008
2008
  data: Record<string, any>;
2009
2009
  } | null>;
2010
- }, keyof UnionToIntersection<Option["plugins"] extends (infer T_10)[] ? T_10 extends BetterAuthPlugin ? T_10 extends {
2010
+ }, UnionToIntersection<Option["plugins"] extends (infer T_10)[] ? T_10 extends BetterAuthPlugin ? T_10 extends {
2011
2011
  endpoints: infer E;
2012
- } ? E : {} : {} : {}>> & UnionToIntersection<Option["plugins"] extends (infer T_10)[] ? T_10 extends BetterAuthPlugin ? T_10 extends {
2013
- endpoints: infer E;
2014
- } ? E : {} : {} : {}>;
2012
+ } ? E : {} : {} : {}>>;
2015
2013
  middlewares: {
2016
2014
  path: string;
2017
2015
  middleware: any;
@@ -2019,7 +2017,11 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
2019
2017
  };
2020
2018
  declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, options: Option) => {
2021
2019
  handler: (request: Request) => Promise<Response>;
2022
- endpoints: Omit<{
2020
+ endpoints: UnionToIntersection<Option["plugins"] extends (infer T)[] ? T extends BetterAuthPlugin ? T extends {
2021
+ endpoints: infer E;
2022
+ } ? E : {} : {} : {}> extends infer T_1 ? T_1 extends UnionToIntersection<Option["plugins"] extends (infer T)[] ? T extends BetterAuthPlugin ? T extends {
2023
+ endpoints: infer E;
2024
+ } ? E : {} : {} : {}> ? T_1 extends unknown ? Prettify$1<({
2023
2025
  readonly ok: better_call0.StrictEndpoint<"/ok", {
2024
2026
  method: "GET";
2025
2027
  metadata: {
@@ -2139,7 +2141,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2139
2141
  emailVerified: boolean;
2140
2142
  name: string;
2141
2143
  image?: string | null | undefined;
2142
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T ? { [K in keyof T]: T[K] } : never) | undefined;
2144
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_3 ? { [K_1 in keyof T_3]: T_3[K_1] } : never) | undefined;
2143
2145
  };
2144
2146
  };
2145
2147
  openapi: {
@@ -2192,7 +2194,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2192
2194
  emailVerified: boolean;
2193
2195
  name: string;
2194
2196
  image?: string | null | undefined;
2195
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_1 ? { [K in keyof T_1]: T_1[K] } : never;
2197
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_4 ? { [K_1 in keyof T_4]: T_4[K_1] } : never;
2196
2198
  }>;
2197
2199
  readonly callbackOAuth: better_call0.StrictEndpoint<"/callback/:id", {
2198
2200
  method: ("GET" | "POST")[];
@@ -2264,7 +2266,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2264
2266
  token: string;
2265
2267
  ipAddress?: string | null | undefined;
2266
2268
  userAgent?: string | null | undefined;
2267
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["session"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"session", Option["plugins"]> extends infer T_2 ? { [K_1 in keyof T_2]: T_2[K_1] } : never;
2269
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["session"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"session", Option["plugins"]> extends infer T_5 ? { [K_2 in keyof T_5]: T_5[K_2] } : never;
2268
2270
  user: {
2269
2271
  id: string;
2270
2272
  createdAt: Date;
@@ -2273,7 +2275,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2273
2275
  emailVerified: boolean;
2274
2276
  name: string;
2275
2277
  image?: string | null | undefined;
2276
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_3 ? { [K in keyof T_3]: T_3[K] } : never;
2278
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_6 ? { [K_1 in keyof T_6]: T_6[K_1] } : never;
2277
2279
  } | null>;
2278
2280
  readonly signOut: better_call0.StrictEndpoint<"/sign-out", {
2279
2281
  method: "POST";
@@ -2338,7 +2340,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2338
2340
  emailVerified: boolean;
2339
2341
  name: string;
2340
2342
  image?: string | null | undefined;
2341
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_4 ? { [K in keyof T_4]: T_4[K] } : never;
2343
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_7 ? { [K_1 in keyof T_7]: T_7[K_1] } : never;
2342
2344
  };
2343
2345
  };
2344
2346
  openapi: {
@@ -2466,7 +2468,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2466
2468
  emailVerified: boolean;
2467
2469
  name: string;
2468
2470
  image?: string | null | undefined;
2469
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_5 ? { [K in keyof T_5]: T_5[K] } : never;
2471
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_8 ? { [K_1 in keyof T_8]: T_8[K_1] } : never;
2470
2472
  } | {
2471
2473
  token: string;
2472
2474
  user: {
@@ -2477,7 +2479,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2477
2479
  emailVerified: boolean;
2478
2480
  name: string;
2479
2481
  image?: string | null | undefined;
2480
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_6 ? { [K in keyof T_6]: T_6[K] } : never;
2482
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_9 ? { [K_1 in keyof T_9]: T_9[K_1] } : never;
2481
2483
  }>;
2482
2484
  readonly signInEmail: better_call0.StrictEndpoint<"/sign-in/email", {
2483
2485
  method: "POST";
@@ -2510,7 +2512,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2510
2512
  emailVerified: boolean;
2511
2513
  name: string;
2512
2514
  image?: string | null | undefined;
2513
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_7 ? { [K in keyof T_7]: T_7[K] } : never;
2515
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_10 ? { [K_1 in keyof T_10]: T_10[K_1] } : never;
2514
2516
  };
2515
2517
  };
2516
2518
  openapi: {
@@ -2562,7 +2564,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2562
2564
  emailVerified: boolean;
2563
2565
  name: string;
2564
2566
  image?: string | null | undefined;
2565
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_8 ? { [K in keyof T_8]: T_8[K] } : never;
2567
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["user"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"user", Option["plugins"]> extends infer T_11 ? { [K_1 in keyof T_11]: T_11[K_1] } : never;
2566
2568
  }>;
2567
2569
  readonly resetPassword: better_call0.StrictEndpoint<"/reset-password", {
2568
2570
  method: "POST";
@@ -3374,7 +3376,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
3374
3376
  token: string;
3375
3377
  ipAddress?: string | null | undefined;
3376
3378
  userAgent?: string | null | undefined;
3377
- } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["session"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"session", Option["plugins"]> extends infer T_9 ? { [K_1 in keyof T_9]: T_9[K_1] } : never>[]>;
3379
+ } & _better_auth_core_db0.InferDBFieldsFromOptions<Option["session"]> & _better_auth_core_db0.InferDBFieldsFromPlugins<"session", Option["plugins"]> extends infer T_12 ? { [K_2 in keyof T_12]: T_12[K_2] } : never>[]>;
3378
3380
  readonly revokeSession: better_call0.StrictEndpoint<"/revoke-session", {
3379
3381
  method: "POST";
3380
3382
  body: zod.ZodObject<{
@@ -3995,11 +3997,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
3995
3997
  user: _better_auth_core_oauth20.OAuth2UserInfo;
3996
3998
  data: Record<string, any>;
3997
3999
  } | null>;
3998
- }, keyof UnionToIntersection<Option["plugins"] extends (infer T_10)[] ? T_10 extends BetterAuthPlugin ? T_10 extends {
3999
- endpoints: infer E;
4000
- } ? E : {} : {} : {}>> & UnionToIntersection<Option["plugins"] extends (infer T_10)[] ? T_10 extends BetterAuthPlugin ? T_10 extends {
4001
- endpoints: infer E;
4002
- } ? E : {} : {} : {}>;
4000
+ } extends infer T_2 ? { [K in keyof T_2 as K extends keyof T_1 ? never : K]: T_2[K] } : never) & T_1> : never : never : never;
4003
4001
  };
4004
4002
  //#endregion
4005
4003
  export { APIError, type AuthEndpoint, type AuthMiddleware, accountInfo, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, error, formCsrfMiddleware, freshSessionMiddleware, getAccessToken, getEndpoints, getIp, getOAuthState, getSession, getSessionFromCtx, getShouldSkipSessionRefresh, isAPIError, linkSocialAccount, listSessions, listUserAccounts, ok, optionsMiddleware, originCheck, originCheckMiddleware, refreshToken, requestOnlySessionMiddleware, requestPasswordReset, requestPasswordResetCallback, requireOrgRole, requireResourceOwnership, resetPassword, revokeOtherSessions, revokeSession, revokeSessions, router, sendVerificationEmail, sendVerificationEmailFn, sensitiveSessionMiddleware, sessionMiddleware, setPassword, setShouldSkipSessionRefresh, signInEmail, signInSocial, signOut, signUpEmail, unlinkAccount, updateSession, updateUser, verifyEmail, verifyPassword };
@@ -165,10 +165,9 @@ const router = (ctx, options) => {
165
165
  if (disabledPaths.includes(normalizedPath)) return new Response("Not Found", { status: 404 });
166
166
  let currentRequest = req;
167
167
  for (const plugin of ctx.options.plugins || []) if (plugin.onRequest) {
168
- const response = await withSpan(`onRequest ${normalizedPath} ${plugin.id}`, {
168
+ const response = await withSpan(`onRequest ${plugin.id}`, {
169
169
  [ATTR_HOOK_TYPE]: "onRequest",
170
- [ATTR_CONTEXT]: `plugin:${plugin.id}`,
171
- [ATTR_HTTP_ROUTE]: normalizedPath
170
+ [ATTR_CONTEXT]: `plugin:${plugin.id}`
172
171
  }, () => plugin.onRequest(currentRequest, ctx));
173
172
  if (response && "response" in response) return response.response;
174
173
  if (response && "request" in response) currentRequest = response.request;
@@ -179,12 +178,10 @@ const router = (ctx, options) => {
179
178
  },
180
179
  async onResponse(res, req) {
181
180
  await onResponseRateLimit(req, ctx);
182
- const normalizedPath = normalizePathname(req.url, basePath);
183
181
  for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) {
184
- const response = await withSpan(`onResponse ${normalizedPath} ${plugin.id}`, {
182
+ const response = await withSpan(`onResponse ${plugin.id}`, {
185
183
  [ATTR_HOOK_TYPE]: "onResponse",
186
184
  [ATTR_CONTEXT]: `plugin:${plugin.id}`,
187
- [ATTR_HTTP_ROUTE]: normalizedPath,
188
185
  [ATTR_HTTP_RESPONSE_STATUS_CODE]: res.status
189
186
  }, () => plugin.onResponse(res, ctx));
190
187
  if (response) return response.response;
@@ -104,7 +104,7 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
104
104
  return redirectOnError("unable_to_link_account");
105
105
  }
106
106
  if (userInfo.email?.toLowerCase() !== link.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) return redirectOnError("email_doesn't_match");
107
- const existingAccount = await c.context.internalAdapter.findAccount(String(userInfo.id));
107
+ const existingAccount = await c.context.internalAdapter.findAccountByProviderId(String(userInfo.id), provider.id);
108
108
  if (existingAccount) {
109
109
  if (existingAccount.userId.toString() !== link.userId.toString()) return redirectOnError("account_already_linked_to_different_user");
110
110
  const updateData = Object.fromEntries(Object.entries({
@@ -28,7 +28,7 @@ function toAuthEndpoints(endpoints, ctx) {
28
28
  const run = async () => {
29
29
  const authContext = await ctx;
30
30
  const methodName = context?.method ?? context?.request?.method ?? defaultMethod ?? "?";
31
- const pathName = context?.path ?? endpoint.path ?? "/:virtual";
31
+ const route = endpoint.path ?? "/:virtual";
32
32
  let internalContext = {
33
33
  ...context,
34
34
  context: {
@@ -42,8 +42,8 @@ function toAuthEndpoints(endpoints, ctx) {
42
42
  };
43
43
  const hasRequest = context?.request instanceof Request;
44
44
  const shouldReturnResponse = context?.asResponse ?? hasRequest;
45
- return withSpan(`${methodName} ${pathName}`, {
46
- [ATTR_HTTP_ROUTE]: pathName,
45
+ return withSpan(`${methodName} ${route}`, {
46
+ [ATTR_HTTP_ROUTE]: route,
47
47
  [ATTR_OPERATION_ID]: operationId
48
48
  }, async () => runWithEndpointContext(internalContext, async () => {
49
49
  const { beforeHooks, afterHooks } = getHooks(authContext);
@@ -70,8 +70,8 @@ function toAuthEndpoints(endpoints, ctx) {
70
70
  internalContext.asResponse = false;
71
71
  internalContext.returnHeaders = true;
72
72
  internalContext.returnStatus = true;
73
- const result = await runWithEndpointContext(internalContext, () => withSpan(`handler ${pathName}`, {
74
- [ATTR_HTTP_ROUTE]: pathName,
73
+ const result = await runWithEndpointContext(internalContext, () => withSpan(`handler ${route}`, {
74
+ [ATTR_HTTP_ROUTE]: route,
75
75
  [ATTR_OPERATION_ID]: operationId
76
76
  }, () => endpoint(internalContext))).catch((e) => {
77
77
  if (isAPIError(e))
@@ -130,10 +130,10 @@ async function runBeforeHooks(context, hooks, endpoint, operationId) {
130
130
  }
131
131
  if (matched) {
132
132
  const hookSource = hooksSourceWeakMap.get(hook.handler) ?? "unknown";
133
- const path = context.path ?? endpoint?.path ?? "/:virtual";
134
- const result = await withSpan(`hook before ${path} ${hookSource}`, {
133
+ const route = endpoint.path ?? "/:virtual";
134
+ const result = await withSpan(`hook before ${route} ${hookSource}`, {
135
135
  [ATTR_HOOK_TYPE]: "before",
136
- [ATTR_HTTP_ROUTE]: path,
136
+ [ATTR_HTTP_ROUTE]: route,
137
137
  [ATTR_CONTEXT]: hookSource,
138
138
  [ATTR_OPERATION_ID]: operationId
139
139
  }, () => hook.handler({
@@ -162,10 +162,10 @@ async function runBeforeHooks(context, hooks, endpoint, operationId) {
162
162
  async function runAfterHooks(context, hooks, endpoint, operationId) {
163
163
  for (const hook of hooks) if (hook.matcher(context)) {
164
164
  const hookSource = hooksSourceWeakMap.get(hook.handler) ?? "unknown";
165
- const path = context.path ?? endpoint?.path ?? "/:virtual";
166
- const result = await withSpan(`hook after ${path} ${hookSource}`, {
165
+ const route = endpoint.path ?? "/:virtual";
166
+ const result = await withSpan(`hook after ${route} ${hookSource}`, {
167
167
  [ATTR_HOOK_TYPE]: "after",
168
- [ATTR_HTTP_ROUTE]: path,
168
+ [ATTR_HTTP_ROUTE]: route,
169
169
  [ATTR_CONTEXT]: hookSource,
170
170
  [ATTR_OPERATION_ID]: operationId
171
171
  }, () => hook.handler(context)).catch((e) => {
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../types/helper.mjs";
2
2
  import { CamelCase, InferCtx, InferRoute, InferRoutes, InferSignUpEmailCtx, InferUserUpdateCtx, MergeRoutes, PathToObject, ProxyRequest } from "./path-to-object.mjs";
3
3
  import { BetterAuthClientOptions, BetterAuthClientPlugin, ClientAtomListener, ClientStore, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferSessionFromClient, InferUserFromClient, IsSignal, SessionQueryParams } from "./types.mjs";
4
4
  import { BroadcastChannel, BroadcastListener, BroadcastMessage, getGlobalBroadcastChannel, kBroadcastChannel } from "./broadcast-channel.mjs";
@@ -31,4 +31,4 @@ declare function InferAuth<O extends {
31
31
  options: BetterAuthOptions;
32
32
  }>(): O["options"];
33
33
  //#endregion
34
- export { AccessControl, ArrayElement, AuthClient, AuthQueryAtom, AuthorizeResponse, BetterAuthClientOptions, BetterAuthClientPlugin, BroadcastChannel, BroadcastListener, BroadcastMessage, CamelCase, ClientAtomListener, ClientStore, type DBPrimitive, DefaultOrganizationPlugin, DynamicAccessControlEndpoints, ExtractPluginField, type FocusListener, type FocusManager, HasRequiredKeys, InferActions, InferAdditionalFromClient, InferAuth, InferClientAPI, InferCtx, InferErrorCodes, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPlugin, InferPluginFieldFromTuple, InferRoute, InferRoutes, InferSessionFromClient, InferSignUpEmailCtx, InferTeam, InferUserFromClient, InferUserUpdateCtx, Invitation, InvitationInput, InvitationStatus, IsAny, IsSignal, Member, MemberInput, MergeRoutes, type OnlineListener, type OnlineManager, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, PathToObject, Prettify, PrettifyDeep, ProxyRequest, RequiredKeysOf, Role, SessionQueryParams, SessionRefreshOptions, SessionResponse, Statements, StripEmptyObjects, SubArray, Subset, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, type UnionToIntersection, createAccessControl, createAuthClient, createSessionRefreshManager, defaultRolesSchema, getGlobalBroadcastChannel, getOrgAdapter, hasPermission, invitationSchema, invitationStatus, kBroadcastChannel, kFocusManager, kOnlineManager, memberSchema, organization, organizationRoleSchema, organizationSchema, parseJSON, parseRoles, role, roleSchema, teamMemberSchema, teamSchema, useAuthQuery };
34
+ export { AccessControl, ArrayElement, AuthClient, AuthQueryAtom, AuthorizeResponse, BetterAuthClientOptions, BetterAuthClientPlugin, BroadcastChannel, BroadcastListener, BroadcastMessage, CamelCase, ClientAtomListener, ClientStore, type DBPrimitive, DefaultOrganizationPlugin, DynamicAccessControlEndpoints, ExtractPluginField, type FocusListener, type FocusManager, HasRequiredKeys, InferActions, InferAdditionalFromClient, InferAuth, InferClientAPI, InferCtx, InferErrorCodes, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPlugin, InferPluginFieldFromTuple, InferRoute, InferRoutes, InferSessionFromClient, InferSignUpEmailCtx, InferTeam, InferUserFromClient, InferUserUpdateCtx, Invitation, InvitationInput, InvitationStatus, IsAny, IsSignal, Member, MemberInput, MergeRoutes, type OnlineListener, type OnlineManager, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, OverrideMerge, PathToObject, Prettify, PrettifyDeep, ProxyRequest, RequiredKeysOf, Role, SessionQueryParams, SessionRefreshOptions, SessionResponse, Statements, StripEmptyObjects, SubArray, Subset, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, type UnionToIntersection, createAccessControl, createAuthClient, createSessionRefreshManager, defaultRolesSchema, getGlobalBroadcastChannel, getOrgAdapter, hasPermission, invitationSchema, invitationStatus, kBroadcastChannel, kFocusManager, kOnlineManager, memberSchema, organization, organizationRoleSchema, organizationSchema, parseJSON, parseRoles, role, roleSchema, teamMemberSchema, teamSchema, useAuthQuery };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
2
2
  import { InferActions, InferClientAPI, InferErrorCodes, IsSignal, SessionQueryParams } from "../types.mjs";
3
3
  import { useStore } from "./lynx-store.mjs";
4
4
  import { BetterAuthClientOptions, BetterAuthClientPlugin } from "@better-auth/core";
@@ -123,4 +123,4 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
123
123
  $ERROR_CODES: PrettifyDeep<InferErrorCodes<Option> & typeof BASE_ERROR_CODES>;
124
124
  };
125
125
  //#endregion
126
- export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient, useStore };
126
+ export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient, useStore };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
2
2
  import { InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferTeam, Invitation, InvitationInput, InvitationStatus, Member, MemberInput, Organization, OrganizationInput, OrganizationRole, OrganizationSchema, Team, TeamInput, TeamMember, TeamMemberInput, defaultRolesSchema, invitationSchema, invitationStatus, memberSchema, organizationRoleSchema, organizationSchema, roleSchema, teamMemberSchema, teamSchema } from "../../plugins/organization/schema.mjs";
3
3
  import { AdminOptions, InferAdminRolesFromOption, SessionWithImpersonatedBy, UserWithRole } from "../../plugins/admin/types.mjs";
4
4
  import { schema } from "../../plugins/anonymous/schema.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, 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, 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 };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
2
2
  import { InferActions, InferClientAPI, InferErrorCodes, IsSignal, SessionQueryParams } from "../types.mjs";
3
3
  import { useStore } from "./react-store.mjs";
4
4
  import { BetterAuthClientOptions, BetterAuthClientPlugin } from "@better-auth/core";
@@ -124,4 +124,4 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
124
124
  $ERROR_CODES: InferErrorCodes<Option> & typeof BASE_ERROR_CODES;
125
125
  };
126
126
  //#endregion
127
- export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient, useStore };
127
+ export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient, useStore };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
2
2
  import { InferActions, InferClientAPI, InferErrorCodes, IsSignal, SessionQueryParams } from "../types.mjs";
3
3
  import { BetterAuthClientOptions, BetterAuthClientPlugin } from "@better-auth/core";
4
4
  import { BASE_ERROR_CODES } from "@better-auth/core/error";
@@ -118,4 +118,4 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
118
118
  $ERROR_CODES: PrettifyDeep<InferErrorCodes<Option> & typeof BASE_ERROR_CODES>;
119
119
  };
120
120
  //#endregion
121
- export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient };
121
+ export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
2
2
  import { InferActions, InferClientAPI, InferErrorCodes, IsSignal, SessionQueryParams } from "../types.mjs";
3
3
  import { BetterAuthClientOptions, BetterAuthClientPlugin } from "@better-auth/core";
4
4
  import { BASE_ERROR_CODES } from "@better-auth/core/error";
@@ -124,4 +124,4 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
124
124
  $ERROR_CODES: PrettifyDeep<InferErrorCodes<Option> & typeof BASE_ERROR_CODES>;
125
125
  };
126
126
  //#endregion
127
- export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient };
127
+ export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../../types/helper.mjs";
2
2
  import { InferActions, InferClientAPI, InferErrorCodes, IsSignal, SessionQueryParams } from "../types.mjs";
3
3
  import { BetterAuthClientOptions, BetterAuthClientPlugin } from "@better-auth/core";
4
4
  import { BASE_ERROR_CODES } from "@better-auth/core/error";
@@ -147,4 +147,4 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
147
147
  $ERROR_CODES: PrettifyDeep<InferErrorCodes<Option> & typeof BASE_ERROR_CODES>;
148
148
  };
149
149
  //#endregion
150
- export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient };
150
+ export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, type UnionToIntersection, createAuthClient };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "./types/helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "./types/helper.mjs";
2
2
  import { BetterAuthClientOptions, BetterAuthClientPlugin, ClientAtomListener, ClientStore, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferSessionFromClient, InferUserFromClient, IsSignal, SessionQueryParams } from "./client/types.mjs";
3
3
  import { DBAdapter, DBAdapterInstance, DBAdapterSchemaCreation, DBTransactionAdapter, JoinConfig, JoinOption, Where } from "./types/adapter.mjs";
4
4
  import { FilteredAPI, InferAPI, InferSessionAPI } from "./types/api.mjs";
@@ -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, Prettify, PrettifyDeep, RateLimit, RequiredKeysOf, Session, SessionQueryParams, type StandardSchemaV1, StateData, StoreIdentifierOption, StripEmptyObjects, type TelemetryEvent, UnionToIntersection, User, Verification, Where, betterAuth, createTelemetry, generateGenericState, generateState, getBaseURL, getCurrentAdapter, getHost, getHostFromRequest, getOrigin, getProtocol, getProtocolFromRequest, getTelemetryAuthConfig, isDynamicBaseURLConfig, matchesHostPattern, parseGenericState, parseState, resolveBaseURL, resolveDynamicBaseURL };
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, getHostFromRequest, getOrigin, getProtocol, getProtocolFromRequest, getTelemetryAuthConfig, isDynamicBaseURLConfig, matchesHostPattern, parseGenericState, parseState, resolveBaseURL, resolveDynamicBaseURL };
@@ -24,20 +24,29 @@ const nextCookies = () => {
24
24
  matcher(ctx) {
25
25
  return ctx.path === "/get-session";
26
26
  },
27
- handler: createAuthMiddleware(async () => {
28
- let cookieStore;
27
+ handler: createAuthMiddleware(async (ctx) => {
28
+ if ("_flag" in ctx && ctx._flag === "router") return;
29
+ let headersStore;
29
30
  try {
30
- const { cookies } = await import("next/headers.js");
31
- cookieStore = await cookies();
31
+ const { headers } = await import("next/headers.js");
32
+ headersStore = await headers();
32
33
  } catch {
33
34
  return;
34
35
  }
35
- try {
36
- cookieStore.set("__better-auth-cookie-store", "1", { maxAge: 0 });
37
- cookieStore.delete("__better-auth-cookie-store");
38
- } catch {
39
- await setShouldSkipSessionRefresh(true);
40
- }
36
+ /**
37
+ * Detect RSC via headers, NOT by probing cookies().set().
38
+ * In Next.js, cookies().set() unconditionally triggers router
39
+ * cache invalidation -- even if the value is unchanged.
40
+ *
41
+ * RSC sends `RSC: 1` without `next-action`. Only in that
42
+ * context cookies cannot be written -- skip session refresh
43
+ * to avoid DB/cookie mismatch.
44
+ *
45
+ * @see https://github.com/vercel/next.js/blob/8c5af211d580/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts#L112-L157
46
+ */
47
+ const isRSC = headersStore.get("RSC") === "1";
48
+ const isServerAction = !!headersStore.get("next-action");
49
+ if (isRSC && !isServerAction) await setShouldSkipSessionRefresh(true);
41
50
  })
42
51
  }],
43
52
  after: [{
@@ -51,12 +60,12 @@ const nextCookies = () => {
51
60
  const setCookies = returned?.get("set-cookie");
52
61
  if (!setCookies) return;
53
62
  const parsed = parseSetCookieHeader(setCookies);
54
- const { cookies } = await import("next/headers.js");
55
63
  let cookieHelper;
56
64
  try {
65
+ const { cookies } = await import("next/headers.js");
57
66
  cookieHelper = await cookies();
58
67
  } catch (error) {
59
- if (error instanceof Error && error.message.startsWith("`cookies` was called outside a request scope.")) return;
68
+ if (error instanceof Error && (error.message.startsWith("`cookies` was called outside a request scope.") || error.message.includes("Cannot find module"))) return;
60
69
  throw error;
61
70
  }
62
71
  parsed.forEach((value, key) => {
@@ -15,6 +15,7 @@ declare function parseState(c: GenericEndpointContext): Promise<{
15
15
  expiresAt: number;
16
16
  errorURL?: string | undefined;
17
17
  newUserURL?: string | undefined;
18
+ oauthState?: string | undefined;
18
19
  link?: {
19
20
  email: string;
20
21
  userId: string;
package/dist/package.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "1.6.0";
2
+ var version = "1.6.2";
3
3
  //#endregion
4
4
  export { version };
@@ -164,6 +164,10 @@ const oAuthProxy = (opts) => {
164
164
  return;
165
165
  }
166
166
  const errorURL = stateData.errorURL || ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;
167
+ if (stateData.oauthState !== void 0 && stateData.oauthState !== statePackage.state) {
168
+ ctx.context.logger.error("OAuth proxy state binding mismatch");
169
+ throw redirectOnError(ctx, errorURL, "state_mismatch");
170
+ }
167
171
  if (error) throw redirectOnError(ctx, errorURL, error);
168
172
  if (!code) {
169
173
  ctx.context.logger.error("OAuth callback missing authorization code");
@@ -19,8 +19,18 @@ declare const twoFactorClient: (options?: {
19
19
  /**
20
20
  * a redirect function to call if a user needs to verify
21
21
  * their two factor
22
+ *
23
+ * @param context.twoFactorMethods - The list of
24
+ * enabled two factor providers (e.g. ["totp", "otp"]).
25
+ * Use this to determine which 2FA UI to show.
22
26
  */
23
- onTwoFactorRedirect?: () => void | Promise<void>;
27
+ onTwoFactorRedirect?: (context: {
28
+ /**
29
+ * The list of enabled two factor providers
30
+ * for the user (e.g. ["totp", "otp"]).
31
+ */
32
+ twoFactorMethods?: string[];
33
+ }) => void | Promise<void>;
24
34
  } | undefined) => {
25
35
  id: "two-factor";
26
36
  version: string;
@@ -26,7 +26,7 @@ const twoFactorClient = (options) => {
26
26
  hooks: { async onSuccess(context) {
27
27
  if (context.data?.twoFactorRedirect) {
28
28
  if (options?.onTwoFactorRedirect) {
29
- await options.onTwoFactorRedirect();
29
+ await options.onTwoFactorRedirect({ twoFactorMethods: context.data.twoFactorMethods });
30
30
  return;
31
31
  }
32
32
  if (options?.twoFactorPage && typeof window !== "undefined") window.location.href = options.twoFactorPage;
@@ -618,6 +618,7 @@ declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
618
618
  matcher(context: _better_auth_core0.HookEndpointContext): boolean;
619
619
  handler: (inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
620
620
  twoFactorRedirect: boolean;
621
+ twoFactorMethods: string[];
621
622
  } | undefined>;
622
623
  }[];
623
624
  };
@@ -655,6 +656,12 @@ declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
655
656
  };
656
657
  index: true;
657
658
  };
659
+ verified: {
660
+ type: "boolean";
661
+ required: false;
662
+ defaultValue: true;
663
+ input: false;
664
+ };
658
665
  };
659
666
  };
660
667
  };
@@ -103,6 +103,13 @@ const twoFactor = (options) => {
103
103
  });
104
104
  await ctx.context.internalAdapter.deleteSession(ctx.context.session.session.token);
105
105
  }
106
+ const existingTwoFactor = await ctx.context.adapter.findOne({
107
+ model: opts.twoFactorTable,
108
+ where: [{
109
+ field: "userId",
110
+ value: user.id
111
+ }]
112
+ });
106
113
  await ctx.context.adapter.deleteMany({
107
114
  model: opts.twoFactorTable,
108
115
  where: [{
@@ -115,7 +122,8 @@ const twoFactor = (options) => {
115
122
  data: {
116
123
  secret: encryptedSecret,
117
124
  backupCodes: backupCodes.encryptedBackupCodes,
118
- userId: user.id
125
+ userId: user.id,
126
+ verified: existingTwoFactor != null && existingTwoFactor.verified !== false || !!options?.skipVerificationOnEnable
119
127
  }
120
128
  });
121
129
  const totpURI = createOTP(secret, {
@@ -225,7 +233,30 @@ const twoFactor = (options) => {
225
233
  expiresAt: new Date(Date.now() + maxAge * 1e3)
226
234
  });
227
235
  await ctx.setSignedCookie(twoFactorCookie.name, identifier, ctx.context.secret, twoFactorCookie.attributes);
228
- return ctx.json({ twoFactorRedirect: true });
236
+ const twoFactorMethods = [];
237
+ /**
238
+ * totp requires per-user setup, so we check
239
+ * that the user actually has a secret stored.
240
+ */
241
+ if (!options?.totpOptions?.disable) {
242
+ const userTotpSecret = await ctx.context.adapter.findOne({
243
+ model: opts.twoFactorTable,
244
+ where: [{
245
+ field: "userId",
246
+ value: data.user.id
247
+ }]
248
+ });
249
+ if (userTotpSecret && userTotpSecret.verified !== false) twoFactorMethods.push("totp");
250
+ }
251
+ /**
252
+ * otp is server-level — if sendOTP is configured,
253
+ * any user with 2fa enabled can receive a code.
254
+ */
255
+ if (options?.otpOptions?.sendOTP) twoFactorMethods.push("otp");
256
+ return ctx.json({
257
+ twoFactorRedirect: true,
258
+ twoFactorMethods
259
+ });
229
260
  })
230
261
  }] },
231
262
  schema: mergeSchema(schema, {
@@ -175,11 +175,11 @@ const otp2fa = (options) => {
175
175
  if (!session.session) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION);
176
176
  const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { twoFactorEnabled: true });
177
177
  const newSession = await ctx.context.internalAdapter.createSession(session.user.id, false, session.session);
178
- await ctx.context.internalAdapter.deleteSession(session.session.token);
179
178
  await setSessionCookie(ctx, {
180
179
  session: newSession,
181
180
  user: updatedUser
182
181
  });
182
+ await ctx.context.internalAdapter.deleteSession(session.session.token);
183
183
  return ctx.json({
184
184
  token: newSession.token,
185
185
  user: parseUserOutput(ctx.context.options, updatedUser)
@@ -33,6 +33,12 @@ declare const schema: {
33
33
  };
34
34
  index: true;
35
35
  };
36
+ verified: {
37
+ type: "boolean";
38
+ required: false;
39
+ defaultValue: true;
40
+ input: false;
41
+ };
36
42
  };
37
43
  };
38
44
  };
@@ -27,6 +27,12 @@ const schema = {
27
27
  field: "id"
28
28
  },
29
29
  index: true
30
+ },
31
+ verified: {
32
+ type: "boolean",
33
+ required: false,
34
+ defaultValue: true,
35
+ input: false
30
36
  }
31
37
  } }
32
38
  };
@@ -124,6 +124,7 @@ const totp2fa = (options) => {
124
124
  }
125
125
  const { session, valid, invalid } = await verifyTwoFactor(ctx);
126
126
  const user = session.user;
127
+ const isSignIn = !session.session;
127
128
  const twoFactor = await ctx.context.adapter.findOne({
128
129
  model: twoFactorTable,
129
130
  where: [{
@@ -132,6 +133,7 @@ const totp2fa = (options) => {
132
133
  }]
133
134
  });
134
135
  if (!twoFactor) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED);
136
+ if (isSignIn && twoFactor.verified === false) throw APIError.from("BAD_REQUEST", TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED);
135
137
  if (!await createOTP(await symmetricDecrypt({
136
138
  key: ctx.context.secretConfig,
137
139
  data: twoFactor.secret
@@ -139,16 +141,23 @@ const totp2fa = (options) => {
139
141
  period: opts.period,
140
142
  digits: opts.digits
141
143
  }).verify(ctx.body.code)) return invalid("INVALID_CODE");
142
- if (!user.twoFactorEnabled) {
143
- if (!session.session) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION);
144
- const updatedUser = await ctx.context.internalAdapter.updateUser(user.id, { twoFactorEnabled: true });
145
- const newSession = await ctx.context.internalAdapter.createSession(user.id, false, session.session).catch((e) => {
146
- throw e;
147
- });
148
- await ctx.context.internalAdapter.deleteSession(session.session.token);
149
- await setSessionCookie(ctx, {
150
- session: newSession,
151
- user: updatedUser
144
+ if (twoFactor.verified !== true) {
145
+ if (!user.twoFactorEnabled) {
146
+ const activeSession = session.session;
147
+ const updatedUser = await ctx.context.internalAdapter.updateUser(user.id, { twoFactorEnabled: true });
148
+ await setSessionCookie(ctx, {
149
+ session: await ctx.context.internalAdapter.createSession(user.id, false, activeSession),
150
+ user: updatedUser
151
+ });
152
+ await ctx.context.internalAdapter.deleteSession(activeSession.token);
153
+ }
154
+ await ctx.context.adapter.update({
155
+ model: twoFactorTable,
156
+ update: { verified: true },
157
+ where: [{
158
+ field: "id",
159
+ value: twoFactor.id
160
+ }]
152
161
  });
153
162
  }
154
163
  return valid(ctx);
@@ -80,7 +80,7 @@ interface TwoFactorTable {
80
80
  userId: string;
81
81
  secret: string;
82
82
  backupCodes: string;
83
- enabled: boolean;
83
+ verified: boolean;
84
84
  }
85
85
  //#endregion
86
86
  export { TwoFactorOptions, TwoFactorProvider, TwoFactorTable, UserWithTwoFactor };
package/dist/state.d.mts CHANGED
@@ -9,6 +9,11 @@ declare const stateDataSchema: z.ZodObject<{
9
9
  errorURL: z.ZodOptional<z.ZodString>;
10
10
  newUserURL: z.ZodOptional<z.ZodString>;
11
11
  expiresAt: z.ZodNumber;
12
+ /**
13
+ * CSRF nonce returned to the OAuth provider. When using cookie state storage,
14
+ * this must match the callback `state` query parameter.
15
+ */
16
+ oauthState: z.ZodOptional<z.ZodString>;
12
17
  link: z.ZodOptional<z.ZodObject<{
13
18
  email: z.ZodString;
14
19
  userId: z.ZodCoercedString<unknown>;
@@ -32,6 +37,7 @@ declare function parseGenericState(c: GenericEndpointContext, state: string, set
32
37
  expiresAt: number;
33
38
  errorURL?: string | undefined;
34
39
  newUserURL?: string | undefined;
40
+ oauthState?: string | undefined;
35
41
  link?: {
36
42
  email: string;
37
43
  userId: string;
package/dist/state.mjs CHANGED
@@ -10,6 +10,7 @@ const stateDataSchema = z.looseObject({
10
10
  errorURL: z.string().optional(),
11
11
  newUserURL: z.string().optional(),
12
12
  expiresAt: z.number(),
13
+ oauthState: z.string().optional(),
13
14
  link: z.object({
14
15
  email: z.string(),
15
16
  userId: z.coerce.string()
@@ -28,9 +29,13 @@ var StateError = class extends BetterAuthError {
28
29
  async function generateGenericState(c, stateData, settings) {
29
30
  const state = generateRandomString(32);
30
31
  if (c.context.oauthConfig.storeStateStrategy === "cookie") {
32
+ const payload = {
33
+ ...stateData,
34
+ oauthState: state
35
+ };
31
36
  const encryptedData = await symmetricEncrypt({
32
37
  key: c.context.secretConfig,
33
- data: JSON.stringify(stateData)
38
+ data: JSON.stringify(payload)
34
39
  });
35
40
  const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "oauth_state", { maxAge: 600 });
36
41
  c.setCookie(stateCookie.name, encryptedData, stateCookie.attributes);
@@ -44,7 +49,10 @@ async function generateGenericState(c, stateData, settings) {
44
49
  const expiresAt = /* @__PURE__ */ new Date();
45
50
  expiresAt.setMinutes(expiresAt.getMinutes() + 10);
46
51
  if (!await c.context.internalAdapter.createVerificationValue({
47
- value: JSON.stringify(stateData),
52
+ value: JSON.stringify({
53
+ ...stateData,
54
+ oauthState: state
55
+ }),
48
56
  identifier: state,
49
57
  expiresAt
50
58
  })) throw new StateError("Unable to create verification. Make sure the database adapter is properly working and there is a verification table in the database", { code: "state_generation_error" });
@@ -76,6 +84,10 @@ async function parseGenericState(c, state, settings) {
76
84
  cause: error
77
85
  });
78
86
  }
87
+ if (!parsedData.oauthState || parsedData.oauthState !== state) throw new StateError("State mismatch: OAuth state parameter does not match stored state", {
88
+ code: "state_security_mismatch",
89
+ details: { state }
90
+ });
79
91
  expireCookie(c, stateCookie);
80
92
  } else {
81
93
  const data = await c.context.internalAdapter.findVerificationValue(state);
@@ -84,6 +96,10 @@ async function parseGenericState(c, state, settings) {
84
96
  details: { state }
85
97
  });
86
98
  parsedData = stateDataSchema.parse(JSON.parse(data.value));
99
+ if (parsedData.oauthState !== void 0 && parsedData.oauthState !== state) throw new StateError("State mismatch: OAuth state parameter does not match stored state", {
100
+ code: "state_security_mismatch",
101
+ details: { state }
102
+ });
87
103
  const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "state");
88
104
  const stateCookieValue = await c.getSignedCookie(stateCookie.name, c.context.secret);
89
105
  if (!(settings?.skipStateCookieCheck ?? c.context.oauthConfig.skipStateCookieCheck) && (!stateCookieValue || stateCookieValue !== state)) throw new StateError("State mismatch: State not persisted correctly", {
@@ -43,7 +43,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
43
43
  fetchOptions: {
44
44
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
45
45
  };
46
- })["plugins"] extends any[] ? Omit<InferAPI<Omit<{
46
+ })["plugins"] extends any[] ? Omit<InferAPI<Prettify$1<{
47
47
  readonly ok: better_call0.StrictEndpoint<"/ok", {
48
48
  method: "GET";
49
49
  metadata: {
@@ -2019,7 +2019,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
2019
2019
  user: _better_auth_core_oauth20.OAuth2UserInfo;
2020
2020
  data: Record<string, any>;
2021
2021
  } | null>;
2022
- }, never>>, keyof (((C extends undefined ? {} : C) & {
2022
+ }>>, keyof (((C extends undefined ? {} : C) & {
2023
2023
  baseURL: string | undefined;
2024
2024
  fetchOptions: {
2025
2025
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
@@ -2047,7 +2047,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
2047
2047
  $InferServerPlugin: infer Plug;
2048
2048
  } ? Plug extends {
2049
2049
  endpoints: infer Endpoints;
2050
- } ? Endpoints : {} : {}> : {} : never : never) : InferAPI<Omit<{
2050
+ } ? Endpoints : {} : {}> : {} : never : never) : InferAPI<Prettify$1<{
2051
2051
  readonly ok: better_call0.StrictEndpoint<"/ok", {
2052
2052
  method: "GET";
2053
2053
  metadata: {
@@ -4023,7 +4023,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
4023
4023
  user: _better_auth_core_oauth20.OAuth2UserInfo;
4024
4024
  data: Record<string, any>;
4025
4025
  } | null>;
4026
- }, never>>, (C extends undefined ? {} : C) & {
4026
+ }>>, (C extends undefined ? {} : C) & {
4027
4027
  baseURL: string | undefined;
4028
4028
  fetchOptions: {
4029
4029
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
@@ -4054,7 +4054,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
4054
4054
  fetchOptions: {
4055
4055
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
4056
4056
  };
4057
- })["plugins"] extends any[] ? Omit<InferAPI<Omit<{
4057
+ })["plugins"] extends any[] ? Omit<InferAPI<Prettify$1<{
4058
4058
  readonly ok: better_call0.StrictEndpoint<"/ok", {
4059
4059
  method: "GET";
4060
4060
  metadata: {
@@ -6030,7 +6030,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
6030
6030
  user: _better_auth_core_oauth20.OAuth2UserInfo;
6031
6031
  data: Record<string, any>;
6032
6032
  } | null>;
6033
- }, never>>, keyof (((C extends undefined ? {} : C) & {
6033
+ }>>, keyof (((C extends undefined ? {} : C) & {
6034
6034
  baseURL: string | undefined;
6035
6035
  fetchOptions: {
6036
6036
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
@@ -6058,7 +6058,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
6058
6058
  $InferServerPlugin: infer Plug;
6059
6059
  } ? Plug extends {
6060
6060
  endpoints: infer Endpoints;
6061
- } ? Endpoints : {} : {}> : {} : never : never) : InferAPI<Omit<{
6061
+ } ? Endpoints : {} : {}> : {} : never : never) : InferAPI<Prettify$1<{
6062
6062
  readonly ok: better_call0.StrictEndpoint<"/ok", {
6063
6063
  method: "GET";
6064
6064
  metadata: {
@@ -8034,7 +8034,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
8034
8034
  user: _better_auth_core_oauth20.OAuth2UserInfo;
8035
8035
  data: Record<string, any>;
8036
8036
  } | null>;
8037
- }, never>>, (C extends undefined ? {} : C) & {
8037
+ }>>, (C extends undefined ? {} : C) & {
8038
8038
  baseURL: string | undefined;
8039
8039
  fetchOptions: {
8040
8040
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
@@ -8136,7 +8136,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
8136
8136
  fetchOptions: {
8137
8137
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
8138
8138
  };
8139
- })["plugins"] extends any[] ? Omit<InferAPI<Omit<{
8139
+ })["plugins"] extends any[] ? Omit<InferAPI<Prettify$1<{
8140
8140
  readonly ok: better_call0.StrictEndpoint<"/ok", {
8141
8141
  method: "GET";
8142
8142
  metadata: {
@@ -10112,7 +10112,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
10112
10112
  user: _better_auth_core_oauth20.OAuth2UserInfo;
10113
10113
  data: Record<string, any>;
10114
10114
  } | null>;
10115
- }, never>>, keyof (((C extends undefined ? {} : C) & {
10115
+ }>>, keyof (((C extends undefined ? {} : C) & {
10116
10116
  baseURL: string | undefined;
10117
10117
  fetchOptions: {
10118
10118
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
@@ -10140,7 +10140,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
10140
10140
  $InferServerPlugin: infer Plug;
10141
10141
  } ? Plug extends {
10142
10142
  endpoints: infer Endpoints;
10143
- } ? Endpoints : {} : {}> : {} : never : never) : InferAPI<Omit<{
10143
+ } ? Endpoints : {} : {}> : {} : never : never) : InferAPI<Prettify$1<{
10144
10144
  readonly ok: better_call0.StrictEndpoint<"/ok", {
10145
10145
  method: "GET";
10146
10146
  metadata: {
@@ -12116,7 +12116,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
12116
12116
  user: _better_auth_core_oauth20.OAuth2UserInfo;
12117
12117
  data: Record<string, any>;
12118
12118
  } | null>;
12119
- }, never>>, (C extends undefined ? {} : C) & {
12119
+ }>>, (C extends undefined ? {} : C) & {
12120
12120
  baseURL: string | undefined;
12121
12121
  fetchOptions: {
12122
12122
  customFetchImpl: (url: string | URL | Request, init?: RequestInit | undefined) => Promise<Response>;
@@ -6,6 +6,13 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
6
6
  type RequiredKeysOf<BaseType extends object> = Exclude<{ [Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]> ? Key : never }[keyof BaseType], undefined>;
7
7
  type HasRequiredKeys<BaseType> = IsAny<BaseType> extends true ? false : [BaseType] extends [object] ? RequiredKeysOf<BaseType & object> extends never ? false : true : false;
8
8
  type StripEmptyObjects<T extends object> = { [K in keyof T]: T[K] };
9
+ /**
10
+ * Object merge replacing `Base`'s keys with `Override`'s.
11
+ * The naive `Omit<Base, keyof Override> & Override` form breaks under generics.
12
+ *
13
+ * @see https://github.com/microsoft/TypeScript/issues/57466#issuecomment-1957988380
14
+ */
15
+ type OverrideMerge<Base, Override> = Base extends unknown ? Override extends unknown ? Prettify<{ [K in keyof Base as K extends keyof Override ? never : K]: Base[K] } & Override> : never : never;
9
16
  /**
10
17
  * Extracts a Record-typed field from a plugin, guarding against `any`.
11
18
  */
@@ -16,4 +23,4 @@ type ExtractPluginField<T, Field extends string> = IsAny<T> extends true ? {} :
16
23
  */
17
24
  type InferPluginFieldFromTuple<T extends readonly unknown[], Field extends string, Acc = {}> = T extends readonly [infer Head, ...infer Tail] ? InferPluginFieldFromTuple<Tail, Field, Acc & ExtractPluginField<Head, Field>> : Acc;
18
25
  //#endregion
19
- export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection };
26
+ export { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection };
@@ -1,4 +1,4 @@
1
- import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "./helper.mjs";
1
+ import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "./helper.mjs";
2
2
  import { BetterAuthClientOptions, BetterAuthClientPlugin, ClientAtomListener, ClientStore, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferSessionFromClient, InferUserFromClient, IsSignal, SessionQueryParams } from "../client/types.mjs";
3
3
  import { DBAdapter, DBAdapterInstance, DBAdapterSchemaCreation, DBTransactionAdapter, JoinConfig, JoinOption, Where } from "./adapter.mjs";
4
4
  import { FilteredAPI, InferAPI, InferSessionAPI } from "./api.mjs";
@@ -7,4 +7,4 @@ import { InferOptionSchema, InferPluginContext, InferPluginErrorCodes, InferPlug
7
7
  import { Auth } from "./auth.mjs";
8
8
  import { BetterAuthAdvancedOptions, BetterAuthCookies, BetterAuthOptions, BetterAuthPlugin, BetterAuthRateLimitOptions, StoreIdentifierOption } from "@better-auth/core";
9
9
  export * from "@better-auth/core/social-providers";
10
- export { Account, AdditionalSessionFieldsInput, AdditionalUserFieldsInput, Auth, type BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, type BetterAuthCookies, type BetterAuthOptions, type BetterAuthPlugin, type BetterAuthRateLimitOptions, ClientAtomListener, ClientStore, DBAdapter, DBAdapterInstance, DBAdapterSchemaCreation, DBTransactionAdapter, ExtractPluginField, FilteredAPI, HasRequiredKeys, InferAPI, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferOptionSchema, InferPluginContext, InferPluginErrorCodes, InferPluginFieldFromTuple, InferPluginIDs, InferPluginTypes, InferSessionAPI, InferSessionFromClient, InferUserFromClient, IsAny, IsSignal, JoinConfig, JoinOption, Prettify, PrettifyDeep, RateLimit, RequiredKeysOf, Session, SessionQueryParams, type StoreIdentifierOption, StripEmptyObjects, UnionToIntersection, User, Verification, Where };
10
+ export { Account, AdditionalSessionFieldsInput, AdditionalUserFieldsInput, Auth, type BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, type BetterAuthCookies, type BetterAuthOptions, type BetterAuthPlugin, type BetterAuthRateLimitOptions, ClientAtomListener, ClientStore, DBAdapter, DBAdapterInstance, DBAdapterSchemaCreation, DBTransactionAdapter, ExtractPluginField, FilteredAPI, HasRequiredKeys, InferAPI, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferOptionSchema, InferPluginContext, InferPluginErrorCodes, InferPluginFieldFromTuple, InferPluginIDs, InferPluginTypes, InferSessionAPI, InferSessionFromClient, InferUserFromClient, IsAny, IsSignal, JoinConfig, JoinOption, OverrideMerge, Prettify, PrettifyDeep, RateLimit, RequiredKeysOf, Session, SessionQueryParams, type StoreIdentifierOption, StripEmptyObjects, UnionToIntersection, User, Verification, Where };
@@ -12,10 +12,14 @@ async function validatePassword(ctx, data) {
12
12
  async function checkPassword(userId, c) {
13
13
  const credentialAccount = (await c.context.internalAdapter.findAccounts(userId))?.find((account) => account.providerId === "credential");
14
14
  const currentPassword = credentialAccount?.password;
15
- if (!credentialAccount || !currentPassword || !c.body.password) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND);
15
+ const password = c.body.password;
16
+ if (!credentialAccount || !currentPassword || !password) {
17
+ if (password) await c.context.password.hash(password);
18
+ throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
19
+ }
16
20
  if (!await c.context.password.verify({
17
21
  hash: currentPassword,
18
- password: c.body.password
22
+ password
19
23
  })) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
20
24
  return true;
21
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-auth",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "The most comprehensive authentication framework for TypeScript.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -489,13 +489,13 @@
489
489
  "kysely": "^0.28.14",
490
490
  "nanostores": "^1.1.1",
491
491
  "zod": "^4.3.6",
492
- "@better-auth/core": "1.6.0",
493
- "@better-auth/drizzle-adapter": "1.6.0",
494
- "@better-auth/kysely-adapter": "1.6.0",
495
- "@better-auth/memory-adapter": "1.6.0",
496
- "@better-auth/mongo-adapter": "1.6.0",
497
- "@better-auth/prisma-adapter": "1.6.0",
498
- "@better-auth/telemetry": "1.6.0"
492
+ "@better-auth/core": "1.6.2",
493
+ "@better-auth/drizzle-adapter": "1.6.2",
494
+ "@better-auth/kysely-adapter": "1.6.2",
495
+ "@better-auth/memory-adapter": "1.6.2",
496
+ "@better-auth/mongo-adapter": "1.6.2",
497
+ "@better-auth/prisma-adapter": "1.6.2",
498
+ "@better-auth/telemetry": "1.6.2"
499
499
  },
500
500
  "devDependencies": {
501
501
  "@lynx-js/react": "^0.116.3",