better-auth 1.7.6 → 1.7.8

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.
@@ -41,12 +41,11 @@ interface OAuthProxyOptions {
41
41
  * A dedicated secret used to encrypt and decrypt data passed between
42
42
  * servers during the OAuth proxy flow.
43
43
  *
44
- * When set, this secret is used **instead of** the global
45
- * `BETTER_AUTH_SECRET` for all OAuth proxy encryption operations.
46
- * This limits the blast radius if the secret is shared across
47
- * environments (production, preview, development): a leaked proxy
48
- * secret cannot be used to forge sessions or decrypt other data
49
- * protected by the main secret.
44
+ * When set, keys derived from this secret are used **instead of** keys
45
+ * derived from `BETTER_AUTH_SECRET` for OAuth proxy encryption.
46
+ * This limits exposure of data protected only by the main secret if
47
+ * the proxy secret is shared across environments (production, preview,
48
+ * development). A compromised proxy secret can still affect OAuth flows.
50
49
  *
51
50
  * All environments participating in the OAuth proxy flow must share
52
51
  * the same `secret` value.
@@ -9,7 +9,8 @@ import { redirectOnError } from "../../oauth2/errors.mjs";
9
9
  import { getOAuthCallbackPath } from "../../oauth2/utils.mjs";
10
10
  import { handleOAuthUserInfo, linkOAuthAccount } from "../../oauth2/link-account.mjs";
11
11
  import { setOAuthState } from "../../api/state/oauth.mjs";
12
- import { parseGenericState } from "../../state.mjs";
12
+ import { derivePurposeKey } from "../../crypto/purpose.mjs";
13
+ import { getAuthStateVerificationIdentifier, parseGenericState } from "../../state.mjs";
13
14
  import { PACKAGE_VERSION } from "../../version.mjs";
14
15
  import { parseJSON } from "../../client/parser.mjs";
15
16
  import { checkSkipProxy, resolveCurrentURL, stripTrailingSlash } from "./utils.mjs";
@@ -66,7 +67,7 @@ const oauthCallbackQuerySchema = z.object({
66
67
  });
67
68
  const oAuthProxy = (opts) => {
68
69
  const maxAge = opts?.maxAge ?? 60;
69
- const getEncryptionKey = (ctx) => opts?.secret ?? ctx.context.secretConfig;
70
+ const getEncryptionKey = (ctx, purpose) => derivePurposeKey(opts?.secret ?? ctx.context.secretConfig, purpose);
70
71
  const oauthProxyCompletion = createAuthEndpoint("/callback/:id/oauth-proxy", {
71
72
  method: "GET",
72
73
  operationId: "oauthProxyCompletion",
@@ -84,7 +85,7 @@ const oAuthProxy = (opts) => {
84
85
  let decryptedPayload;
85
86
  try {
86
87
  decryptedPayload = await symmetricDecrypt({
87
- key: getEncryptionKey(ctx),
88
+ key: getEncryptionKey(ctx, "oauth-proxy-profile"),
88
89
  data: encryptedProfile
89
90
  });
90
91
  } catch (e) {
@@ -225,7 +226,7 @@ const oAuthProxy = (opts) => {
225
226
  let statePackage;
226
227
  try {
227
228
  statePackage = parseJSON(await symmetricDecrypt({
228
- key: getEncryptionKey(ctx),
229
+ key: getEncryptionKey(ctx, "oauth-proxy-package"),
229
230
  data: state
230
231
  }));
231
232
  } catch {
@@ -245,7 +246,7 @@ const oAuthProxy = (opts) => {
245
246
  let stateData;
246
247
  try {
247
248
  stateData = parseJSON(await symmetricDecrypt({
248
- key: getEncryptionKey(ctx),
249
+ key: getEncryptionKey(ctx, "oauth-proxy-state"),
249
250
  data: statePackage.stateCookie
250
251
  }));
251
252
  } catch (e) {
@@ -338,7 +339,7 @@ const oAuthProxy = (opts) => {
338
339
  timestamp: Date.now()
339
340
  };
340
341
  const encryptedPayload = await symmetricEncrypt({
341
- key: getEncryptionKey(ctx),
342
+ key: getEncryptionKey(ctx, "oauth-proxy-profile"),
342
343
  data: JSON.stringify(payload)
343
344
  });
344
345
  proxyCallbackURL.searchParams.set("profile", encryptedPayload);
@@ -366,11 +367,11 @@ const oAuthProxy = (opts) => {
366
367
  const oauthStateCookie = ctx.context.createAuthCookie("oauth_state");
367
368
  const encryptedCookieValue = parseSetCookieHeader(setCookieHeader).get(oauthStateCookie.name)?.value;
368
369
  if (encryptedCookieValue) plaintextState = await symmetricDecrypt({
369
- key: ctx.context.secretConfig,
370
+ key: derivePurposeKey(ctx.context.secretConfig, "oauth-state-cookie"),
370
371
  data: encryptedCookieValue
371
372
  });
372
373
  }
373
- } else plaintextState = (await ctx.context.internalAdapter.findVerificationValue(originalState))?.value;
374
+ } else plaintextState = (await ctx.context.internalAdapter.findVerificationValue(getAuthStateVerificationIdentifier(originalState)))?.value;
374
375
  if (!plaintextState) {
375
376
  ctx.context.logger.warn("No OAuth state found for proxy");
376
377
  return;
@@ -378,13 +379,13 @@ const oAuthProxy = (opts) => {
378
379
  const statePackage = {
379
380
  state: originalState,
380
381
  stateCookie: await symmetricEncrypt({
381
- key: getEncryptionKey(ctx),
382
+ key: getEncryptionKey(ctx, "oauth-proxy-state"),
382
383
  data: plaintextState
383
384
  }),
384
385
  isOAuthProxy: true
385
386
  };
386
387
  const encryptedPackage = await symmetricEncrypt({
387
- key: getEncryptionKey(ctx),
388
+ key: getEncryptionKey(ctx, "oauth-proxy-package"),
388
389
  data: JSON.stringify(statePackage)
389
390
  });
390
391
  oauthURL.searchParams.set("state", encryptedPackage);
@@ -1,5 +1,6 @@
1
1
  import { PACKAGE_VERSION } from "../../version.mjs";
2
2
  import { useAuthQuery } from "../../client/query.mjs";
3
+ import { matchesSessionSignal } from "../../client/config.mjs";
3
4
  import { adminAc, defaultRoles, memberAc, ownerAc } from "./access/statement.mjs";
4
5
  import { ORGANIZATION_ERROR_CODES } from "./error-codes.mjs";
5
6
  import { hasPermissionFn } from "./permission.mjs";
@@ -71,7 +72,7 @@ const organizationClient = (options) => {
71
72
  },
72
73
  {
73
74
  matcher(path) {
74
- return path === "/sign-out" || path.startsWith("/organization");
75
+ return matchesSessionSignal(path) || path.startsWith("/organization");
75
76
  },
76
77
  signal: "$activeOrgSignal"
77
78
  },
@@ -1009,48 +1009,7 @@ declare const getActiveMemberRole: <O extends OrganizationOptions>(options: O) =
1009
1009
  };
1010
1010
  }>>)[];
1011
1011
  }, {
1012
- role: (((O["teams"] extends {
1013
- enabled: true;
1014
- } ? {
1015
- id: string;
1016
- organizationId: string;
1017
- role: InferOrganizationRolesFromOption<O>;
1018
- createdAt: Date;
1019
- userId: string;
1020
- teamId?: string | undefined;
1021
- user: {
1022
- id: string;
1023
- email: string;
1024
- name: string;
1025
- image?: string | undefined;
1026
- };
1027
- } : {
1028
- id: string;
1029
- organizationId: string;
1030
- role: InferOrganizationRolesFromOption<O>;
1031
- createdAt: Date;
1032
- userId: string;
1033
- user: {
1034
- id: string;
1035
- email: string;
1036
- name: string;
1037
- image?: string | undefined;
1038
- };
1039
- }) & (O["schema"] extends {
1040
- member?: {
1041
- additionalFields: infer Field extends Record<string, _$_better_auth_core_db0.DBFieldAttribute>;
1042
- } | undefined;
1043
- } ? FieldAttributeToObject<Field> : {}) extends infer T ? { [K in keyof T]: T[K] } : never) & {
1044
- user: {
1045
- id: string;
1046
- createdAt: Date;
1047
- updatedAt: Date;
1048
- email: string;
1049
- emailVerified: boolean;
1050
- name: string;
1051
- image?: string | null | undefined;
1052
- };
1053
- })["role"];
1012
+ role: InferOrganizationRolesFromOption<O>;
1054
1013
  }>;
1055
1014
  //#endregion
1056
1015
  export { addMember, getActiveMember, getActiveMemberRole, leaveOrganization, listMembers, removeMember, updateMemberRole };
@@ -512,7 +512,7 @@ const getActiveMemberRole = (options) => createAuthEndpoint("/organization/get-a
512
512
  organizationId
513
513
  });
514
514
  if (!member) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION);
515
- return ctx.json({ role: member?.role });
515
+ return ctx.json({ role: member.role });
516
516
  });
517
517
  //#endregion
518
518
  export { addMember, getActiveMember, getActiveMemberRole, leaveOrganization, listMembers, removeMember, updateMemberRole };
@@ -4,7 +4,7 @@ import { generateRandomString } from "../../crypto/random.mjs";
4
4
  import { setSessionCookie } from "../../cookies/index.mjs";
5
5
  import { getSessionFromCtx } from "../../api/routes/session.mjs";
6
6
  import { HIDE_METADATA } from "../../utils/hide-metadata.mjs";
7
- import { assertPasswordNotTooLong, assertPasswordNotTooShort } from "../../utils/password.mjs";
7
+ import { assertPasswordNotTooLong, assertPasswordNotTooShort, resetCredentialPassword } from "../../utils/password.mjs";
8
8
  import { PHONE_NUMBER_ERROR_CODES } from "./error-codes.mjs";
9
9
  import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
10
10
  import { createAuthEndpoint } from "@better-auth/core/api";
@@ -175,15 +175,19 @@ const sendPhoneNumberOTP = (opts) => createAuthEndpoint("/phone-number/send-otp"
175
175
  async function verifyAndConsumePhoneNumberOTP(ctx, opts, body) {
176
176
  const { phoneNumber, code } = body;
177
177
  if (opts.verifyOTP) {
178
- if (!await opts.verifyOTP({
179
- phoneNumber,
180
- code
181
- }, ctx)) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_OTP);
182
- await ctx.context.internalAdapter.deleteVerificationByIdentifier(phoneNumber);
178
+ await verifyWithCustomVerifier(ctx, opts.verifyOTP, phoneNumber, code, phoneNumber);
183
179
  return;
184
180
  }
185
181
  await verifyPhoneNumberOTP(ctx, opts, phoneNumber, code);
186
182
  }
183
+ /** Checks the code with `verifyOTP`, then drops the code stored under `identifier`. */
184
+ async function verifyWithCustomVerifier(ctx, verifyOTP, phoneNumber, code, identifier) {
185
+ if (!await verifyOTP({
186
+ phoneNumber,
187
+ code
188
+ }, ctx)) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_OTP);
189
+ await ctx.context.internalAdapter.deleteVerificationByIdentifier(identifier);
190
+ }
187
191
  /**
188
192
  * ### API Methods
189
193
  *
@@ -459,7 +463,17 @@ const resetPasswordPhoneNumber = (opts) => createAuthEndpoint("/phone-number/res
459
463
  } }
460
464
  } }
461
465
  }, async (ctx) => {
462
- await verifyPhoneNumberOTP(ctx, opts, `${ctx.body.phoneNumber}-request-password-reset`, ctx.body.otp);
466
+ const phoneResetIdentifier = `${ctx.body.phoneNumber}-request-password-reset`;
467
+ let otpConsumed = false;
468
+ if (opts.verifyOTP) {
469
+ assertPasswordNotTooShort(ctx, ctx.body.newPassword);
470
+ assertPasswordNotTooLong(ctx, ctx.body.newPassword);
471
+ await verifyWithCustomVerifier(ctx, opts.verifyOTP, ctx.body.phoneNumber, ctx.body.otp, phoneResetIdentifier);
472
+ otpConsumed = true;
473
+ } else if (!await matchesUsableStoredPhoneNumberOTP(ctx, opts, phoneResetIdentifier, ctx.body.otp)) {
474
+ await verifyPhoneNumberOTP(ctx, opts, phoneResetIdentifier, ctx.body.otp);
475
+ otpConsumed = true;
476
+ }
463
477
  const user = await ctx.context.adapter.findOne({
464
478
  model: "user",
465
479
  where: [{
@@ -471,15 +485,8 @@ const resetPasswordPhoneNumber = (opts) => createAuthEndpoint("/phone-number/res
471
485
  assertPasswordNotTooShort(ctx, ctx.body.newPassword);
472
486
  assertPasswordNotTooLong(ctx, ctx.body.newPassword);
473
487
  const hashedPassword = await ctx.context.password.hash(ctx.body.newPassword);
474
- if (!await ctx.context.internalAdapter.findCredentialAccount(user.id)) await ctx.context.internalAdapter.createAccount({
475
- userId: user.id,
476
- providerId: "credential",
477
- accountId: user.id,
478
- password: hashedPassword
479
- });
480
- else await ctx.context.internalAdapter.updatePassword(user.id, hashedPassword);
488
+ await resetCredentialPassword(ctx, user.id, hashedPassword, otpConsumed ? void 0 : () => verifyPhoneNumberOTP(ctx, opts, phoneResetIdentifier, ctx.body.otp));
481
489
  if (ctx.context.options.emailAndPassword?.onPasswordReset) await ctx.context.options.emailAndPassword.onPasswordReset({ user }, ctx.request);
482
- if (ctx.context.options.emailAndPassword?.revokeSessionsOnPasswordReset) await ctx.context.internalAdapter.deleteUserSessions(user.id);
483
490
  return ctx.json({ status: true });
484
491
  });
485
492
  /**
@@ -518,6 +525,17 @@ async function verifyPhoneNumberOTP(ctx, opts, identifier, providedCode) {
518
525
  throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_OTP);
519
526
  }
520
527
  }
528
+ /**
529
+ * Reports whether the provided code matches a stored, unexpired record with
530
+ * attempts left, without consuming it or counting an attempt.
531
+ */
532
+ async function matchesUsableStoredPhoneNumberOTP(ctx, opts, identifier, providedCode) {
533
+ const existing = await ctx.context.internalAdapter.findVerificationValue(identifier);
534
+ if (!existing || existing.expiresAt < /* @__PURE__ */ new Date()) return false;
535
+ const [otpValue, rawAttempts] = existing.value.split(":");
536
+ if (parseVerificationAttempts(rawAttempts) >= (opts?.allowedAttempts ?? 3)) return false;
537
+ return otpValue === providedCode;
538
+ }
521
539
  function parseVerificationAttempts(value) {
522
540
  const attempts = Number(value ?? 0);
523
541
  return Number.isSafeInteger(attempts) && attempts > 0 ? attempts : 0;
package/dist/state.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { generateRandomString } from "./crypto/random.mjs";
2
2
  import { symmetricDecrypt, symmetricEncrypt } from "./crypto/index.mjs";
3
3
  import { expireCookie } from "./cookies/index.mjs";
4
+ import { derivePurposeKey } from "./crypto/purpose.mjs";
4
5
  import { BetterAuthError } from "@better-auth/core/error";
5
6
  import * as z from "zod";
6
7
  //#region src/state.ts
@@ -32,6 +33,8 @@ const stateDataSchema = z.looseObject({
32
33
  */
33
34
  serverContext: z.record(z.string(), z.unknown()).optional()
34
35
  });
36
+ /** Verification key for database-backed OAuth and SAML relay state. */
37
+ const getAuthStateVerificationIdentifier = (state) => `auth-state:${state}`;
35
38
  const INTERNAL_STATE_KEYS = new Set(Object.keys(stateDataSchema.shape));
36
39
  var StateError = class extends BetterAuthError {
37
40
  code;
@@ -59,7 +62,7 @@ async function generateGenericState(c, stateData, settings) {
59
62
  oauthState: state
60
63
  };
61
64
  const encryptedData = await symmetricEncrypt({
62
- key: c.context.secretConfig,
65
+ key: derivePurposeKey(c.context.secretConfig, "oauth-state-cookie"),
63
66
  data: JSON.stringify(payload)
64
67
  });
65
68
  const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "oauth_state", { maxAge: 600 });
@@ -78,7 +81,7 @@ async function generateGenericState(c, stateData, settings) {
78
81
  ...stateData,
79
82
  oauthState: state
80
83
  }),
81
- identifier: state,
84
+ identifier: getAuthStateVerificationIdentifier(state),
82
85
  expiresAt
83
86
  })) 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" });
84
87
  return {
@@ -99,7 +102,7 @@ async function parseGenericState(c, state, settings) {
99
102
  });
100
103
  try {
101
104
  const decryptedData = await symmetricDecrypt({
102
- key: c.context.secretConfig,
105
+ key: derivePurposeKey(c.context.secretConfig, "oauth-state-cookie"),
103
106
  data: encryptedData
104
107
  });
105
108
  parsedData = stateDataSchema.parse(JSON.parse(decryptedData));
@@ -117,7 +120,7 @@ async function parseGenericState(c, state, settings) {
117
120
  });
118
121
  expireCookie(c, stateCookie);
119
122
  } else {
120
- const data = await c.context.internalAdapter.findVerificationValue(state);
123
+ const data = await c.context.internalAdapter.findVerificationValue(getAuthStateVerificationIdentifier(state));
121
124
  if (!data) throw new StateError("State mismatch: verification not found", {
122
125
  code: "state_mismatch",
123
126
  details: { state }
@@ -136,7 +139,7 @@ async function parseGenericState(c, state, settings) {
136
139
  errorURL: parsedData.errorURL
137
140
  });
138
141
  expireCookie(c, stateCookie);
139
- await c.context.internalAdapter.deleteVerificationByIdentifier(state);
142
+ await c.context.internalAdapter.deleteVerificationByIdentifier(getAuthStateVerificationIdentifier(state));
140
143
  }
141
144
  if (parsedData.expiresAt < Date.now()) throw new StateError("Invalid state: request expired", {
142
145
  code: "state_mismatch",
@@ -146,4 +149,4 @@ async function parseGenericState(c, state, settings) {
146
149
  return parsedData;
147
150
  }
148
151
  //#endregion
149
- export { INTERNAL_STATE_KEYS, StateError, generateGenericState, parseGenericState };
152
+ export { INTERNAL_STATE_KEYS, StateError, generateGenericState, getAuthStateVerificationIdentifier, parseGenericState };
@@ -1,5 +1,5 @@
1
- import { keccak_256 } from "@noble/hashes/sha3.js";
2
1
  import { utf8ToBytes } from "@noble/hashes/utils.js";
2
+ import { keccak_256 } from "@noble/hashes/sha3.js";
3
3
  //#region src/utils/hashing.ts
4
4
  /**
5
5
  * TS implementation of ERC-55 ("Mixed-case checksum address encoding") using @noble/hashes
@@ -2,6 +2,9 @@
2
2
  const middlewareResponse = ({ message, status, code }) => ({ response: new Response(JSON.stringify({
3
3
  message,
4
4
  code
5
- }), { status }) });
5
+ }), {
6
+ status,
7
+ headers: { "Content-Type": "application/json" }
8
+ }) });
6
9
  //#endregion
7
10
  export { middlewareResponse };
@@ -1,3 +1,4 @@
1
+ import { runWithTransaction } from "@better-auth/core/context";
1
2
  import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
2
3
  //#region src/utils/password.ts
3
4
  function assertPasswordNotTooShort(ctx, password) {
@@ -12,6 +13,37 @@ function assertPasswordNotTooLong(ctx, password) {
12
13
  throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
13
14
  }
14
15
  }
16
+ /**
17
+ * Sets a user's credential password after a reset, revoking their sessions
18
+ * when `emailAndPassword.revokeSessionsOnPasswordReset` is enabled.
19
+ *
20
+ * `consumeResetToken` spends the single-use reset token or OTP and throws when
21
+ * it is no longer valid. It runs first inside the same transaction as
22
+ * revocation and the password write, so a failure in any of them leaves all
23
+ * three unchanged and the user can retry. Verification values held only in
24
+ * secondary storage are consumed with `getAndDelete`, which can't roll back,
25
+ * so a later failure there still spends the token. Secondary-storage sessions are
26
+ * removed by an after-commit hook, so they can't roll back with the write.
27
+ * Sessions are revoked before the write so that on an adapter without
28
+ * transactions a failure still keeps the old password rather than committing a
29
+ * new one while existing sessions stay valid.
30
+ */
31
+ async function resetCredentialPassword(ctx, userId, passwordHash, consumeResetToken) {
32
+ await runWithTransaction(ctx.context.adapter, async () => {
33
+ await consumeResetToken?.();
34
+ if (ctx.context.options.emailAndPassword?.revokeSessionsOnPasswordReset) await ctx.context.internalAdapter.deleteUserSessions(userId);
35
+ if (!await ctx.context.internalAdapter.findCredentialAccount(userId)) {
36
+ await ctx.context.internalAdapter.createAccount({
37
+ userId,
38
+ providerId: "credential",
39
+ accountId: userId,
40
+ password: passwordHash
41
+ });
42
+ return;
43
+ }
44
+ await ctx.context.internalAdapter.updatePassword(userId, passwordHash);
45
+ });
46
+ }
15
47
  async function validatePassword(ctx, data) {
16
48
  assertPasswordNotTooLong(ctx, data.password);
17
49
  const credentialAccount = await ctx.context.internalAdapter.findCredentialAccount(data.userId);
@@ -43,4 +75,4 @@ async function shouldRequirePassword(ctx, userId, allowPasswordless) {
43
75
  return Boolean(credentialAccount?.password);
44
76
  }
45
77
  //#endregion
46
- export { assertPasswordNotTooLong, assertPasswordNotTooShort, checkPassword, shouldRequirePassword, validatePassword };
78
+ export { assertPasswordNotTooLong, assertPasswordNotTooShort, checkPassword, resetCredentialPassword, shouldRequirePassword, validatePassword };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-auth",
3
- "version": "1.7.6",
3
+ "version": "1.7.8",
4
4
  "description": "The most comprehensive authentication framework for TypeScript.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -463,23 +463,23 @@
463
463
  }
464
464
  },
465
465
  "dependencies": {
466
- "@better-auth/utils": "0.4.2",
466
+ "@better-auth/utils": "0.5.0",
467
467
  "@better-fetch/fetch": "1.3.2",
468
468
  "@noble/ciphers": "^2.2.0",
469
469
  "@noble/hashes": "^2.2.0",
470
- "better-call": "1.4.0",
470
+ "better-call": "1.4.2",
471
471
  "defu": "^6.1.4",
472
472
  "jose": "^6.2.3",
473
473
  "kysely": "^0.28.17 || ^0.29.0",
474
474
  "nanostores": "^1.3.0",
475
475
  "zod": "^4.5.4",
476
- "@better-auth/core": "1.7.6",
477
- "@better-auth/drizzle-adapter": "1.7.6",
478
- "@better-auth/kysely-adapter": "1.7.6",
479
- "@better-auth/memory-adapter": "1.7.6",
480
- "@better-auth/mongo-adapter": "1.7.6",
481
- "@better-auth/prisma-adapter": "1.7.6",
482
- "@better-auth/telemetry": "1.7.6"
476
+ "@better-auth/core": "1.7.8",
477
+ "@better-auth/drizzle-adapter": "1.7.8",
478
+ "@better-auth/kysely-adapter": "1.7.8",
479
+ "@better-auth/memory-adapter": "1.7.8",
480
+ "@better-auth/mongo-adapter": "1.7.8",
481
+ "@better-auth/prisma-adapter": "1.7.8",
482
+ "@better-auth/telemetry": "1.7.8"
483
483
  },
484
484
  "devDependencies": {
485
485
  "@lynx-js/react": "^0.121.2",
@@ -487,8 +487,8 @@
487
487
  "@opentelemetry/sdk-trace-base": "^2.10.0",
488
488
  "@opentelemetry/sdk-trace-node": "^2.10.0",
489
489
  "@sveltejs/kit": "^2.70.1",
490
- "@tanstack/react-start": "^1.168.4",
491
- "@tanstack/solid-start": "^1.168.4",
490
+ "@tanstack/react-start": "^1.168.60",
491
+ "@tanstack/solid-start": "^1.168.57",
492
492
  "@types/bun": "^1.3.14",
493
493
  "@types/google.accounts": "^0.0.18",
494
494
  "@types/pg": "^8.20.0",
@@ -511,7 +511,7 @@
511
511
  "peerDependencies": {
512
512
  "@lynx-js/react": "*",
513
513
  "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0",
514
- "@sveltejs/kit": "^2.0.0",
514
+ "@sveltejs/kit": "^2.0.0 || ^3.0.0",
515
515
  "@tanstack/react-start": "^1.0.0",
516
516
  "@tanstack/solid-start": "^1.0.0",
517
517
  "drizzle-kit": ">=0.31.4 || >=1.0.0-beta.1",