@nuxtjs/better-auth 0.3.5 → 0.3.7

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/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "configKey": "auth",
5
5
  "compatibility": {
6
6
  "nuxt": ">=4.0.0"
package/dist/module.mjs CHANGED
@@ -13,7 +13,7 @@ import { isCI, isTest } from 'std-env';
13
13
  import { isInternalAssetPath } from '../dist/runtime/internal/auth-route-rules.js';
14
14
  export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
15
15
 
16
- const version = "0.3.5";
16
+ const version = "0.3.7";
17
17
 
18
18
  const CONFIG_EXTENSIONS = [".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"];
19
19
  const CONFIG_EXTENSION_RE = /\.[cm]?[jt]s$/;
@@ -332,8 +332,7 @@ function registerPrepareTypesHook(input) {
332
332
  nodeTsConfig.compilerOptions.paths ||= {};
333
333
  const projectReferenceTypePaths = [
334
334
  join(nuxt.options.buildDir, "types/nitro-imports.d.ts"),
335
- join(nuxt.options.buildDir, "types/auth-database.d.ts"),
336
- join(nuxt.options.buildDir, "types/auth-schema.d.ts")
335
+ join(nuxt.options.buildDir, "types/auth-database.d.ts")
337
336
  ];
338
337
  if (hasHubDb)
339
338
  projectReferenceTypePaths.push(join(nuxt.options.buildDir, "hub/db.d.ts"));
@@ -342,7 +341,8 @@ function registerPrepareTypesHook(input) {
342
341
  "#auth/server": nuxt.options.alias["#auth/server"],
343
342
  "#auth/client": nuxt.options.alias["#auth/client"],
344
343
  "#auth/database": nuxt.options.alias["#auth/database"],
345
- "#auth/schema": nuxt.options.alias["#auth/schema"],
344
+ // An exact .mjs path bypasses declaration lookup in TypeScript paths.
345
+ "#auth/schema": join(nuxt.options.buildDir, "better-auth/schema"),
346
346
  "#auth/route-rules": nuxt.options.alias["#auth/route-rules"]
347
347
  };
348
348
  for (const [key, value] of Object.entries(exactNodeAliases)) {
@@ -607,6 +607,18 @@ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, h
607
607
  const hubCasing = getHubCasing(hub);
608
608
  const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
609
609
  const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
610
+ const { getAuthTables } = await import('better-auth/db');
611
+ const { initGetModelName } = await import('better-auth/adapters');
612
+ const authTables = getAuthTables(authOptions);
613
+ const getModelName = initGetModelName({
614
+ schema: authTables,
615
+ usePlural: schemaOptions.usePlural ?? false
616
+ });
617
+ const modelNames = {};
618
+ for (const name of ["user", "session", "account", "verification"]) {
619
+ if (authTables[name])
620
+ modelNames[name] = getModelName(name);
621
+ }
610
622
  const schemaDir = join(nuxt.options.buildDir, "better-auth");
611
623
  const schemaPathTs = join(schemaDir, `schema.${dialect}.ts`);
612
624
  const schemaPathMjs = join(schemaDir, `schema.${dialect}.mjs`);
@@ -622,6 +634,7 @@ async function setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, h
622
634
  addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
623
635
  addTemplate({ filename: `better-auth/schema.${dialect}.mjs`, getContents: () => schemaCode, write: true });
624
636
  consola.info(`Generated ${dialect} schema (.ts + .mjs)`);
637
+ return modelNames;
625
638
  } catch (error) {
626
639
  throw error instanceof Diagnostic ? error : diagnostics.NUXT_AUTH_SCHEMA_GENERATION_FAILED({ cause: error });
627
640
  }
@@ -755,7 +768,7 @@ function setupRuntimeConfig(input) {
755
768
  if (clientOnly) {
756
769
  const siteUrl = nuxt.options.runtimeConfig.public.siteUrl;
757
770
  if (!siteUrl)
758
- consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your frontend URL");
771
+ consola.warn("clientOnly mode: set runtimeConfig.public.siteUrl (or NUXT_PUBLIC_SITE_URL) to your auth backend URL, or set baseURL in defineClientAuth()");
759
772
  consola.info("clientOnly mode enabled - server utilities (serverAuth, getRequestSession, getUserSession, requireUserSession) are not available");
760
773
  return { secondaryStorageEnabled };
761
774
  }
@@ -914,7 +927,7 @@ export { db }`;
914
927
  return `export function createDatabase() { return undefined }
915
928
  export const db = undefined`;
916
929
  }
917
- function buildSchemaExportCode(hasHubDb, hubDialect) {
930
+ function buildSchemaExportCode(hasHubDb, hubDialect, modelNames = {}) {
918
931
  if (!hasHubDb) {
919
932
  return `export const user = undefined
920
933
  export const session = undefined
@@ -927,13 +940,43 @@ export const schema = undefined
927
940
  import * as generatedSchema from './schema.${hubDialect}.mjs'
928
941
 
929
942
  const getGeneratedTable = name => generatedSchema[name] ?? generatedSchema[\`\${name}s\`]
930
- export const user = getGeneratedTable('user')
931
- export const session = getGeneratedTable('session')
932
- export const account = getGeneratedTable('account')
933
- export const verification = getGeneratedTable('verification')
943
+ export const user = getGeneratedTable(${JSON.stringify(modelNames.user ?? "user")})
944
+ export const session = getGeneratedTable(${JSON.stringify(modelNames.session ?? "session")})
945
+ export const account = getGeneratedTable(${JSON.stringify(modelNames.account ?? "account")})
946
+ export const verification = getGeneratedTable(${JSON.stringify(modelNames.verification ?? "verification")})
934
947
  export const schema = { ...generatedSchema, user, session, account, verification }
935
948
  `;
936
949
  }
950
+ function buildSchemaExportTypes(hasHubDb, hubDialect, modelNames = {}) {
951
+ if (!hasHubDb) {
952
+ return `export declare const user: undefined
953
+ export declare const session: undefined
954
+ export declare const account: undefined
955
+ export declare const verification: undefined
956
+ export declare const schema: undefined
957
+ `;
958
+ }
959
+ return `export * from './schema.${hubDialect}.js'
960
+
961
+ type GeneratedSchema = typeof import('./schema.${hubDialect}.js')
962
+ type GeneratedTable<Name extends string> = Name extends keyof GeneratedSchema
963
+ ? GeneratedSchema[Name]
964
+ : \`\${Name}s\` extends keyof GeneratedSchema
965
+ ? GeneratedSchema[\`\${Name}s\`]
966
+ : undefined
967
+
968
+ export declare const user: GeneratedTable<${JSON.stringify(modelNames.user ?? "user")}>
969
+ export declare const session: GeneratedTable<${JSON.stringify(modelNames.session ?? "session")}>
970
+ export declare const account: GeneratedTable<${JSON.stringify(modelNames.account ?? "account")}>
971
+ export declare const verification: GeneratedTable<${JSON.stringify(modelNames.verification ?? "verification")}>
972
+ export declare const schema: GeneratedSchema & {
973
+ user: typeof user
974
+ session: typeof session
975
+ account: typeof account
976
+ verification: typeof verification
977
+ }
978
+ `;
979
+ }
937
980
  function buildAuthRouteRulesCode(authRouteRules) {
938
981
  return `export const authRouteRules = ${JSON.stringify(authRouteRules, null, 2)}
939
982
  `;
@@ -1165,24 +1208,6 @@ declare module '#auth/database' {
1165
1208
  export function createDatabase(event?: import('${h3TypesPath}').H3Event): BetterAuthOptions['database']
1166
1209
  export const db: ${hasHubDb ? `typeof import('@nuxthub/db')['db']` : "undefined"}
1167
1210
  }
1168
- `
1169
- }, { nitro: true });
1170
- addTypeTemplate({
1171
- filename: "types/auth-schema.d.ts",
1172
- getContents: () => `
1173
- declare module '#auth/schema' {
1174
- export const user: any
1175
- export const session: any
1176
- export const account: any
1177
- export const verification: any
1178
- export const schema: {
1179
- user: any
1180
- session: any
1181
- account: any
1182
- verification: any
1183
- [key: string]: any
1184
- } | undefined
1185
- }
1186
1211
  `
1187
1212
  }, { nitro: true });
1188
1213
  addTypeTemplate({
@@ -1190,7 +1215,6 @@ declare module '#auth/schema' {
1190
1215
  getContents: () => `
1191
1216
  /// <reference path="./nitro-imports.d.ts" />
1192
1217
  /// <reference path="./auth-database.d.ts" />
1193
- /// <reference path="./auth-schema.d.ts" />
1194
1218
  ${hasHubDb ? '/// <reference path="../hub/db.d.ts" />' : ""}
1195
1219
 
1196
1220
  export {}
@@ -1646,6 +1670,7 @@ const module$1 = defineNuxtModule({
1646
1670
  });
1647
1671
  let serverConfigPath = setup.aliases["#auth/server"];
1648
1672
  let clientConfigPath = setup.aliases["#auth/client"];
1673
+ let schemaModelNames;
1649
1674
  if (serverConfigPath && setup.pluginSources.server.length) {
1650
1675
  const serverConfigCode = buildExtendedServerAuthCode(setup.configs.server.path, setup.pluginSources.server);
1651
1676
  serverConfigPath = addTemplate({
@@ -1675,10 +1700,15 @@ const module$1 = defineNuxtModule({
1675
1700
  nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
1676
1701
  const schemaTemplate = addTemplate({
1677
1702
  filename: "better-auth/schema.mjs",
1678
- getContents: () => buildSchemaExportCode(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite"),
1703
+ getContents: () => buildSchemaExportCode(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite", schemaModelNames),
1679
1704
  write: true
1680
1705
  });
1681
1706
  nuxt.options.alias["#auth/schema"] = schemaTemplate.dst;
1707
+ addTemplate({
1708
+ filename: "better-auth/schema.d.ts",
1709
+ getContents: () => buildSchemaExportTypes(setup.database.hasHubDb, setup.database.buildContext?.hubDialect ?? "sqlite", schemaModelNames),
1710
+ write: true
1711
+ });
1682
1712
  if (setup.schemaGeneration)
1683
1713
  await ensureSchemaBootstrap(schemaTemplate.dst, setup.database.buildContext?.hubDialect ?? "sqlite");
1684
1714
  }
@@ -1717,7 +1747,7 @@ const module$1 = defineNuxtModule({
1717
1747
  if (setup.schemaGeneration) {
1718
1748
  if (setup.schemaGeneration.externalizeNuxtHubDatabase)
1719
1749
  registerNuxtHubDatabaseExternalHook(nuxt);
1720
- await setupBetterAuthSchema(
1750
+ schemaModelNames = await setupBetterAuthSchema(
1721
1751
  nuxt,
1722
1752
  serverConfigPath,
1723
1753
  options,
@@ -1,5 +1,6 @@
1
1
  import createAppAuthClient from "#auth/client";
2
2
  import { useRequestURL, useRuntimeConfig } from "#imports";
3
+ import { resolveAuthBaseURL } from "../../internal/auth-base-url.js";
3
4
  import { createVueSafeAuthProxy } from "../internal/vue-safe-auth-proxy.js";
4
5
  let _client = null;
5
6
  let _clientFacade = null;
@@ -27,7 +28,9 @@ export function useRawAuthClient() {
27
28
  const requestURL = useRequestURL();
28
29
  const configuredSiteUrl = runtimeConfig.public.siteUrl;
29
30
  const siteUrl = typeof configuredSiteUrl === "string" && configuredSiteUrl ? configuredSiteUrl : requestURL.origin;
30
- return getClient(siteUrl);
31
+ const clientOnly = runtimeConfig.public.auth?.clientOnly;
32
+ const appBaseURL = runtimeConfig.app?.baseURL;
33
+ return getClient(clientOnly ? siteUrl : resolveAuthBaseURL(siteUrl, appBaseURL));
31
34
  }
32
35
  export function useAuthClient() {
33
36
  const rawClient = useRawAuthClient();
@@ -11,14 +11,30 @@ export function useAuthRequestFetch() {
11
11
  const clientOptions = createAppAuthClient.resolveOptions(siteUrl);
12
12
  const configuredBaseURL = clientOptions.baseURL ?? siteUrl;
13
13
  const baseURL = configuredBaseURL && new URL(configuredBaseURL).pathname.replace(/\/+$/, "") ? configuredBaseURL : joinURL(configuredBaseURL || "/", clientOptions.basePath ?? "/api/auth");
14
- const externalRequestFetch = requestFetch;
15
- return ((request, opts) => {
16
- if (typeof request !== "string" || !/^\/api\/auth(?=\/|$)/.test(request))
17
- return externalRequestFetch(request, opts);
18
- return externalRequestFetch(request.slice("/api/auth".length), {
19
- ...opts,
20
- baseURL,
21
- credentials: "include"
14
+ function wrapRequestFetch(fetch) {
15
+ const methods = /* @__PURE__ */ new Map();
16
+ return new Proxy(fetch, {
17
+ apply(target, thisArg, args) {
18
+ const [request, opts] = args;
19
+ if (typeof request !== "string" || !/^\/api\/auth(?=\/|$)/.test(request))
20
+ return Reflect.apply(target, thisArg, args);
21
+ return Reflect.apply(target, thisArg, [request.slice("/api/auth".length), {
22
+ ...opts,
23
+ baseURL,
24
+ credentials: "include"
25
+ }]);
26
+ },
27
+ get(target, prop, receiver) {
28
+ if (methods.has(prop))
29
+ return methods.get(prop);
30
+ const value = Reflect.get(target, prop, receiver);
31
+ if (typeof value !== "function" || prop !== "raw" && prop !== "create")
32
+ return value;
33
+ const method = prop === "raw" ? wrapRequestFetch(value) : (...args) => wrapRequestFetch(Reflect.apply(value, target, args));
34
+ methods.set(prop, method);
35
+ return method;
36
+ }
22
37
  });
23
- });
38
+ }
39
+ return wrapRequestFetch(requestFetch);
24
40
  }
@@ -74,6 +74,7 @@ export function useUserSession() {
74
74
  return;
75
75
  const previousUser = user.value;
76
76
  user.value = { ...user.value, ...updates };
77
+ const optimisticUser = user.value;
77
78
  if (!rawClient)
78
79
  return;
79
80
  try {
@@ -86,7 +87,8 @@ export function useUserSession() {
86
87
  throw new Error(normalizedError.message);
87
88
  }
88
89
  } catch (error) {
89
- user.value = previousUser;
90
+ if (user.value === optimisticUser)
91
+ user.value = previousUser;
90
92
  if (!(error instanceof Error)) {
91
93
  const normalizedError = normalizeAuthActionError(error);
92
94
  throw new Error(normalizedError.message);
@@ -1,5 +1,6 @@
1
1
  import { defu } from "defu";
2
2
  import { createRouter, toRouteMatcher } from "radix3";
3
+ import { parsePath, parseQuery } from "ufo";
3
4
  import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig } from "#imports";
4
5
  import { shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
5
6
  import { matchesUser } from "../../utils/match-user.js";
@@ -64,11 +65,11 @@ function resolveLoginRedirect(input) {
64
65
  return { to: loginTarget, external: false };
65
66
  if (!loginTarget.startsWith("/") || loginTarget.startsWith("//"))
66
67
  return { to: loginTarget, external: false };
67
- const [beforeHash, hash = ""] = loginTarget.split("#", 2);
68
- const [path, query = ""] = beforeHash.split("?", 2);
68
+ const { pathname: path, search, hash } = parsePath(loginTarget);
69
+ const query = search.slice(1);
69
70
  try {
70
- const params2 = new URLSearchParams(query);
71
- if (params2.has(redirectQueryKey))
71
+ const params = new URLSearchParams(query);
72
+ if (params.has(redirectQueryKey))
72
73
  return { to: loginTarget, external: false };
73
74
  } catch {
74
75
  return { to: loginTarget, external: false };
@@ -76,15 +77,13 @@ function resolveLoginRedirect(input) {
76
77
  if (import.meta.server) {
77
78
  const separator = query ? "&" : "";
78
79
  const encodedRedirect = encodeURIComponent(route.fullPath);
79
- const url = `${path}?${query}${separator}${redirectQueryKey}=${encodedRedirect}${hash ? `#${hash}` : ""}`;
80
+ const encodedKey = encodeURIComponent(redirectQueryKey);
81
+ const url = `${path}?${query}${separator}${encodedKey}=${encodedRedirect}${hash}`;
80
82
  return { to: url, external: true };
81
83
  }
82
- const params = new URLSearchParams(query);
83
- const queryObj = {};
84
- for (const [k, v] of params.entries())
85
- queryObj[k] = v;
84
+ const queryObj = parseQuery(query);
86
85
  queryObj[redirectQueryKey] = route.fullPath;
87
- return { to: { path, query: queryObj, ...hash ? { hash: `#${hash}` } : {} }, external: false };
86
+ return { to: { path, query: queryObj, ...hash ? { hash } : {} }, external: false };
88
87
  }
89
88
  async function getAuthRouteRules() {
90
89
  if (!authRouteRulesPromise) {
@@ -0,0 +1,2 @@
1
+ /** Better Auth treats a path in baseURL as the complete auth endpoint path. */
2
+ export declare function resolveAuthBaseURL(siteUrl: string, appBaseURL?: string): string;
@@ -0,0 +1,5 @@
1
+ import { joinURL } from "ufo";
2
+ export function resolveAuthBaseURL(siteUrl, appBaseURL) {
3
+ const origin = new URL(siteUrl).origin;
4
+ return appBaseURL && appBaseURL !== "/" ? joinURL(origin, appBaseURL, "/api/auth") : origin;
5
+ }
@@ -1,3 +1,5 @@
1
+ import type { Account, Session, User } from 'better-auth/types';
2
+ import type { AnyColumn, Table } from 'drizzle-orm';
1
3
  import { z } from 'zod';
2
4
  export declare const paginationQuerySchema: z.ZodObject<{
3
5
  page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -6,3 +8,8 @@ export declare const paginationQuerySchema: z.ZodObject<{
6
8
  }, z.core.$strip>;
7
9
  export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
8
10
  export declare function sanitizeSearchPattern(search: string): string;
11
+ export type DevtoolsSchema = {
12
+ user?: Table & Record<keyof User, AnyColumn>;
13
+ session?: Table & Record<keyof Session, AnyColumn>;
14
+ account?: Table & Record<keyof Account, AnyColumn>;
15
+ } | undefined;
@@ -21,7 +21,14 @@ export default defineEventHandler(async (event) => {
21
21
  dbQuery.orderBy(desc(schema.account.createdAt)).limit(limit).offset(offset),
22
22
  countQuery
23
23
  ]);
24
- return { accounts, total: totalResult[0]?.count ?? 0, page, limit };
24
+ const safeAccounts = accounts.map((account) => ({
25
+ id: account.id,
26
+ userId: account.userId,
27
+ providerId: account.providerId,
28
+ accountId: account.accountId,
29
+ createdAt: account.createdAt
30
+ }));
31
+ return { accounts: safeAccounts, total: totalResult[0]?.count ?? 0, page, limit };
25
32
  } catch (error) {
26
33
  console.error("[DevTools] Fetch accounts failed:", error);
27
34
  return { accounts: [], total: 0, error: "Failed to fetch accounts" };
@@ -1,3 +1,4 @@
1
1
  import type { ServerEvent } from './nitro-compat.js';
2
2
  export declare function appendCookieHeader(event: ServerEvent, header: string): void;
3
+ export declare function getSetCookieHeaders(headers: Headers): string[];
3
4
  export declare function appendSetCookieHeaders(event: ServerEvent, headers: Headers): void;
@@ -14,7 +14,7 @@ export function appendCookieHeader(event, header) {
14
14
  const responseHeaders = eventWithResponse.res?.headers ?? eventWithResponse.response?.headers;
15
15
  responseHeaders?.append("set-cookie", header);
16
16
  }
17
- function getSetCookieHeaders(headers) {
17
+ export function getSetCookieHeaders(headers) {
18
18
  const cookieHeaders = headers;
19
19
  const getSetCookie = cookieHeaders.getSetCookie;
20
20
  if (getSetCookie)
@@ -1,8 +1,8 @@
1
1
  import { betterAuth, env } from "better-auth";
2
- import { withoutProtocol } from "ufo";
3
2
  import { createDatabase, db } from "#auth/database";
4
3
  import createServerAuth from "#auth/server";
5
4
  import { getRequestHost, getRequestProtocol, useRuntimeConfig } from "../internal/nitro-compat.js";
5
+ import { resolveAuthBaseURL } from "../../internal/auth-base-url.js";
6
6
  import { resolveCustomSecondaryStorageRequirement } from "./custom-secondary-storage.js";
7
7
  const _authCache = /* @__PURE__ */ new Map();
8
8
  const requestAuthKey = Symbol.for("nuxt-better-auth.requestAuth");
@@ -82,33 +82,25 @@ function getNitroOrigin() {
82
82
  let port;
83
83
  if (import.meta.dev)
84
84
  port = process.env.NITRO_PORT || process.env.PORT || "3000";
85
- let protocol = cert && key || !import.meta.dev ? "https" : "http";
85
+ const protocol = cert && key || !import.meta.dev ? "https" : "http";
86
86
  try {
87
87
  if ((import.meta.dev || import.meta.prerender) && process.env.__NUXT_DEV__) {
88
- const origin = JSON.parse(process.env.__NUXT_DEV__).proxy.url;
89
- host = withoutProtocol(origin);
90
- protocol = origin.includes("https") ? "https" : "http";
88
+ const url = new URL(JSON.parse(process.env.__NUXT_DEV__).proxy.url);
89
+ if (url.protocol === "http:" || url.protocol === "https:")
90
+ return url.origin;
91
91
  } else if ((import.meta.dev || import.meta.prerender) && process.env.NUXT_VITE_NODE_OPTIONS) {
92
- const origin = JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS).baseURL.replace("/__nuxt_vite_node__", "");
93
- host = withoutProtocol(origin);
94
- protocol = origin.includes("https") ? "https" : "http";
92
+ const url = new URL(JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS).baseURL);
93
+ if (url.protocol === "http:" || url.protocol === "https:")
94
+ return url.origin;
95
95
  }
96
96
  } catch {
97
97
  }
98
98
  if (!host)
99
99
  return void 0;
100
- if (host.startsWith("[") && host.includes("]:")) {
101
- const lastBracketColon = host.lastIndexOf("]:");
102
- const extractedPort = host.slice(lastBracketColon + 2);
103
- host = host.slice(0, lastBracketColon + 1);
104
- if (extractedPort)
105
- port = extractedPort;
106
- } else if (host.includes(":") && !host.startsWith("[")) {
107
- const hostParts = host.split(":");
108
- port = hostParts.pop();
109
- host = hostParts.join(":");
110
- }
111
- const portSuffix = port ? `:${port}` : "";
100
+ if (!host.startsWith("[") && host.indexOf(":") !== host.lastIndexOf(":"))
101
+ host = `[${host}]`;
102
+ const hasExplicitPort = host.startsWith("[") ? host.includes("]:") : host.includes(":");
103
+ const portSuffix = !hasExplicitPort && port ? `:${port}` : "";
112
104
  return `${protocol}://${host}${portSuffix}`;
113
105
  }
114
106
  function resolveEnvironmentOrigin() {
@@ -161,8 +153,7 @@ function getDevTrustedOrigins() {
161
153
  try {
162
154
  const url = new URL(nitroOrigin);
163
155
  const protocol = url.protocol === "https:" ? "https" : "http";
164
- const port = url.port || "3000";
165
- const localhostOrigin = `${protocol}://localhost:${port}`;
156
+ const localhostOrigin = `${protocol}://localhost${url.port ? `:${url.port}` : ""}`;
166
157
  return dedupeOrigins([localhostOrigin, url.origin]);
167
158
  } catch {
168
159
  return [fallbackOrigin];
@@ -254,7 +245,7 @@ export function serverAuth(event) {
254
245
  ...rateLimit ? { rateLimit } : {},
255
246
  ...database ? { database } : {},
256
247
  secret: betterAuthSecret,
257
- baseURL: siteUrl,
248
+ baseURL: resolveAuthBaseURL(siteUrl, runtimeConfig.app?.baseURL),
258
249
  trustedOrigins
259
250
  };
260
251
  const auth = betterAuth(authOptions);
@@ -1,6 +1,6 @@
1
1
  import { matchesUser } from "../../utils/match-user.js";
2
2
  import { createAuthError } from "../internal/nitro-compat.js";
3
- import { appendCookieHeader, appendSetCookieHeaders } from "../internal/cookie-headers.js";
3
+ import { appendCookieHeader, appendSetCookieHeaders, getSetCookieHeaders } from "../internal/cookie-headers.js";
4
4
  import { serverAuth } from "./auth.js";
5
5
  const requestSessionLoadKey = Symbol.for("nuxt-better-auth.requestSessionLoad");
6
6
  const requestSessionMutationKey = Symbol.for("nuxt-better-auth.requestSessionMutation");
@@ -162,6 +162,30 @@ function expireCookies(event, cookie) {
162
162
  for (const cookieName of getChunkedCookieNames(event, cookie.name))
163
163
  expireCookie(event, cookieName, cookie.attributes);
164
164
  }
165
+ function isCookieOrChunk(name, cookieName) {
166
+ if (name === cookieName)
167
+ return true;
168
+ if (!name.startsWith(`${cookieName}.`))
169
+ return false;
170
+ const suffix = name.slice(cookieName.length + 1);
171
+ const index = Number(suffix);
172
+ return Number.isSafeInteger(index) && index >= 0 && String(index) === suffix;
173
+ }
174
+ function expireUnrefreshedSessionCookieCache(event, headers, cookie) {
175
+ const cookieName = getCookieName(cookie.name, cookie.attributes.prefix);
176
+ if (!cookieName)
177
+ return;
178
+ const responseHandlesCache = getSetCookieHeaders(headers).some((header) => {
179
+ const name = header.slice(0, header.indexOf("="));
180
+ return isCookieOrChunk(name, cookieName);
181
+ });
182
+ if (responseHandlesCache)
183
+ return;
184
+ for (const name of parseRequestCookies(getRequestHeaders(event).get("cookie")).keys()) {
185
+ if (isCookieOrChunk(name, cookieName))
186
+ expireCookie(event, name, cookie.attributes);
187
+ }
188
+ }
165
189
  function updateRequestHeaders(event, sessionCookie, clearedCookieNames) {
166
190
  const requestContext = getRequestSessionContext(event);
167
191
  const incomingHeaders = getIncomingRequestHeaders(event);
@@ -227,10 +251,14 @@ export async function refreshSessionCookieCache(event) {
227
251
  if (context[requestSessionLoadKey] !== load)
228
252
  return context.requestSession ?? null;
229
253
  delete context.requestSession;
230
- const { headers, response } = await loadFreshSession(event);
254
+ const [{ headers, response }, authContext] = await Promise.all([
255
+ loadFreshSession(event),
256
+ getServerAuthContext(event)
257
+ ]);
231
258
  if (context[requestSessionLoadKey] !== load)
232
259
  return context.requestSession ?? null;
233
260
  appendSetCookieHeaders(event, headers);
261
+ expireUnrefreshedSessionCookieCache(event, headers, authContext.authCookies.sessionData);
234
262
  context.requestSession = response;
235
263
  return response;
236
264
  });
@@ -8,10 +8,6 @@ declare module '#imports' {
8
8
  export function useRuntimeConfig(): any
9
9
  }
10
10
 
11
- declare module '#auth/schema' {
12
- export const schema: any
13
- }
14
-
15
11
  declare module '#auth/server' {
16
12
  const createServerAuth: any
17
13
  export default createServerAuth
@@ -1,9 +1,10 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { fileURLToPath } from "node:url";
3
- import { url } from "@nuxt/test-utils/e2e";
3
+ import { url, useTestContext } from "@nuxt/test-utils/e2e";
4
4
  import { parseJSON } from "better-auth/client";
5
5
  import { getRandomPort } from "get-port-please";
6
6
  import { defu } from "defu";
7
+ import { joinURL } from "ufo";
7
8
  export async function createAuthTestContext(options = {}) {
8
9
  if (options.host || options.dev || options.build === false || options.server === false)
9
10
  throw new Error("[nuxt-better-auth] Auth E2E helpers require a local test build. Use setup() with build and server enabled, without dev or host.");
@@ -22,7 +23,8 @@ export async function createAuthTestContext(options = {}) {
22
23
  }
23
24
  }, options);
24
25
  async function call(action, data = {}) {
25
- const response = await fetch(url(endpoint), {
26
+ const baseURL = useTestContext().nuxt?.options.app.baseURL ?? "/";
27
+ const response = await fetch(url(joinURL(baseURL, endpoint)), {
26
28
  method: "POST",
27
29
  headers: { "content-type": "application/json", "x-nuxt-auth-test": token },
28
30
  body: JSON.stringify({ action, data })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
3
  "type": "module",
4
- "version": "0.3.5",
4
+ "version": "0.3.7",
5
5
  "description": "Nuxt module for Better Auth integration with NuxtHub, route protection, session management, and role-based access",
6
6
  "author": "onmax",
7
7
  "license": "MIT",
@@ -75,7 +75,7 @@
75
75
  "@nuxt/devtools-kit": "^3.4.1",
76
76
  "@nuxt/kit": "^4.5.2",
77
77
  "@types/node": "^24.13.3",
78
- "auth": "1.7.3",
78
+ "auth": "1.7.7",
79
79
  "consola": "^3.4.2",
80
80
  "cookie-es": "^3.1.1",
81
81
  "defu": "^6.1.7",
@@ -102,7 +102,7 @@
102
102
  "@pinia/nuxt": "^1.0.2",
103
103
  "@types/better-sqlite3": "^9.6.0",
104
104
  "@vue/test-utils": "^2.4.6",
105
- "better-auth": "1.7.3",
105
+ "better-auth": "1.7.7",
106
106
  "better-call": "1.4.0",
107
107
  "better-sqlite3": "^12.11.1",
108
108
  "drizzle-kit": "^0.31.10",