@nuxtjs/better-auth 0.3.10 → 0.3.11

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.d.mts CHANGED
@@ -30,24 +30,27 @@ interface BetterAuthDatabaseProviderDefinition {
30
30
  isEnabled?: (ctx: BetterAuthDatabaseProviderEnabledContext) => boolean;
31
31
  priority?: number;
32
32
  }
33
+ interface BetterAuthNuxtHooks {
34
+ /** Register absolute plugin module paths during module setup. Sources are additive and are not deduplicated. */
35
+ 'better-auth:plugins:extend': (sources: BetterAuthPluginSources) => void | Promise<void>;
36
+ /**
37
+ * Add plugin schemas to generated Better Auth database tables.
38
+ * This build-time hook does not install plugins in the runtime auth instance.
39
+ * @param config - Plugins to include during schema generation
40
+ * @param config.plugins - Better Auth plugins whose schemas should be generated
41
+ */
42
+ 'better-auth:config:extend': (config: {
43
+ plugins?: BetterAuthPlugin[];
44
+ }) => void | Promise<void>;
45
+ /** Register or override Better Auth database providers. */
46
+ 'better-auth:database:providers': (providers: Record<string, BetterAuthDatabaseProviderDefinition>) => void | Promise<void>;
47
+ }
33
48
  declare module '@nuxt/schema' {
34
- interface NuxtHooks {
35
- /** Register absolute plugin module paths during module setup. Sources are additive and are not deduplicated. */
36
- 'better-auth:plugins:extend': (sources: BetterAuthPluginSources) => void | Promise<void>;
37
- /**
38
- * Add plugin schemas to generated Better Auth database tables.
39
- * This build-time hook does not install plugins in the runtime auth instance.
40
- * @param config - Plugins to include during schema generation
41
- * @param config.plugins - Better Auth plugins whose schemas should be generated
42
- */
43
- 'better-auth:config:extend': (config: {
44
- plugins?: BetterAuthPlugin[];
45
- }) => void | Promise<void>;
46
- /**
47
- * Register or override Better Auth database providers.
48
- * Providers are auto-selected via `isEnabled` + `priority`.
49
- */
50
- 'better-auth:database:providers': (providers: Record<string, BetterAuthDatabaseProviderDefinition>) => void | Promise<void>;
49
+ interface NuxtHooks extends BetterAuthNuxtHooks {
50
+ }
51
+ }
52
+ declare module 'nuxt/schema' {
53
+ interface NuxtHooks extends BetterAuthNuxtHooks {
51
54
  }
52
55
  }
53
56
 
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
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.10";
16
+ const version = "0.3.11";
17
17
 
18
18
  const CONFIG_EXTENSIONS = [".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"];
19
19
  const CONFIG_EXTENSION_RE = /\.[cm]?[jt]s$/;
@@ -1573,6 +1573,8 @@ declare module '#nuxt-better-auth' {
1573
1573
  emailVerified: boolean
1574
1574
  name: string
1575
1575
  image?: string | null
1576
+ /** Whether the user has enabled two-factor authentication. */
1577
+ twoFactorEnabled?: boolean
1576
1578
  }
1577
1579
 
1578
1580
  export interface AuthSession {
@@ -2,7 +2,7 @@ import { computed, navigateTo, nextTick, useNuxtApp, useRequestURL, useRuntimeCo
2
2
  import { normalizeAuthActionError } from "../internal/auth-action-error.js";
3
3
  import { resolvePostAuthSuccessRedirect, withFallbackSocialCallbackURL } from "../internal/redirect-helpers.js";
4
4
  import { fetchSessionClient, fetchSessionServer, isExpectedSignedOutSessionError, stripToken } from "../internal/session-fetch.js";
5
- import { isRecord } from "../internal/utils.js";
5
+ import { isRecord, setIfChanged } from "../internal/utils.js";
6
6
  import { createVueSafeAuthFacade, isAuthProxyProbeKey } from "../internal/vue-safe-auth-proxy.js";
7
7
  import { wrapAuthMethod } from "../internal/wrap-auth-method.js";
8
8
  import { getAuthRuntimeFlags, useRawAuthClient } from "./useAuthClient.js";
@@ -152,8 +152,8 @@ export function useUserSession() {
152
152
  if (newSession?.data?.session && newSession?.data?.user) {
153
153
  sessionExpiredRedirecting = false;
154
154
  hydrationSessionWasActive = false;
155
- session.value = stripToken(newSession.data.session);
156
- user.value = newSession.data.user;
155
+ setIfChanged(session, stripToken(newSession.data.session));
156
+ setIfChanged(user, newSession.data.user);
157
157
  } else if (!newSession?.isPending && !newSession?.isRefetching) {
158
158
  const isHydrationEmptySnapshot = nuxtApp.isHydrating && nuxtApp.payload.serverRendered && Boolean(session.value && user.value) && !newSession?.data?.session && !newSession?.data?.user;
159
159
  if (isHydrationEmptySnapshot) {
@@ -1,6 +1,7 @@
1
1
  import { parseJSON } from "better-auth/client";
2
2
  import { useRequestFetch, useRequestHeaders } from "#imports";
3
3
  import { normalizeAuthActionError } from "./auth-action-error.js";
4
+ import { setIfChanged } from "./utils.js";
4
5
  export function stripToken(session) {
5
6
  const { token: _, ...safe } = session;
6
7
  return safe;
@@ -35,8 +36,8 @@ export async function fetchSessionServer(session, user, authReady, options = {})
35
36
  ...options.force ? { query: { disableCookieCache: true } } : {}
36
37
  });
37
38
  if (data?.session && data?.user) {
38
- session.value = stripToken(data.session);
39
- user.value = data.user;
39
+ setIfChanged(session, stripToken(data.session));
40
+ setIfChanged(user, data.user);
40
41
  } else {
41
42
  session.value = null;
42
43
  user.value = null;
@@ -60,8 +61,8 @@ export async function fetchSessionClient(client, session, user, authReady, optio
60
61
  }
61
62
  const data = result.data;
62
63
  if (data?.session && data?.user) {
63
- session.value = stripToken(data.session);
64
- user.value = data.user;
64
+ setIfChanged(session, stripToken(data.session));
65
+ setIfChanged(user, data.user);
65
66
  } else {
66
67
  session.value = null;
67
68
  user.value = null;
@@ -1 +1,9 @@
1
1
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
2
+ /**
3
+ * Better Auth refetches the session on window focus and returns fresh objects even
4
+ * when nothing changed. Keeping the current object avoids retriggering every
5
+ * computed and watcher that depends on the session or user.
6
+ */
7
+ export declare function setIfChanged<T>(target: {
8
+ value: T;
9
+ }, value: T): void;
@@ -1,3 +1,19 @@
1
1
  export function isRecord(value) {
2
2
  return Boolean(value && typeof value === "object");
3
3
  }
4
+ function isSameValue(a, b) {
5
+ if (Object.is(a, b))
6
+ return true;
7
+ if (a instanceof Date || b instanceof Date)
8
+ return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
9
+ if (Array.isArray(a) || Array.isArray(b))
10
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => isSameValue(item, b[index]));
11
+ if (!isRecord(a) || !isRecord(b))
12
+ return false;
13
+ const keys = Object.keys(a);
14
+ return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && isSameValue(a[key], b[key]));
15
+ }
16
+ export function setIfChanged(target, value) {
17
+ if (!isSameValue(target.value, value))
18
+ target.value = value;
19
+ }
@@ -2,13 +2,15 @@ import { defu } from "defu";
2
2
  import { createRouter, toRouteMatcher } from "radix3";
3
3
  import { parsePath, parseQuery } from "ufo";
4
4
  import { createError, defineNuxtRouteMiddleware, getRouteRules, navigateTo, useNuxtApp, useRequestHeaders, useRuntimeConfig } from "#imports";
5
- import { shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
5
+ import { normalizeAuthRoutePath, shouldSkipAuthRouteRules } from "../../internal/auth-route-rules.js";
6
6
  import { matchesUser } from "../../utils/match-user.js";
7
7
  import { useUserSession } from "../composables/useUserSession.js";
8
8
  let authRouteRulesPromise = null;
9
9
  let routeRulesMatcherPromise = null;
10
10
  export default defineNuxtRouteMiddleware(async (to) => {
11
- if (shouldSkipAuthRouteRules(to.path))
11
+ const runtimeConfig = useRuntimeConfig();
12
+ const path = normalizeAuthRoutePath(to.path, runtimeConfig.app?.baseURL);
13
+ if (shouldSkipAuthRouteRules(to.path) || shouldSkipAuthRouteRules(path))
12
14
  return;
13
15
  if (to.matched?.length === 0)
14
16
  return;
@@ -30,7 +32,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
30
32
  const auth = to.meta.auth;
31
33
  if (auth === void 0 || auth === false)
32
34
  return;
33
- const config = useRuntimeConfig().public.auth;
35
+ const config = runtimeConfig.public.auth;
34
36
  if (import.meta.server && config?.clientOnly)
35
37
  return;
36
38
  const { fetchSession, user, loggedIn } = useUserSession();
@@ -12,7 +12,7 @@ export interface ClientAuthContext {
12
12
  export type ServerAuthConfig = Omit<BetterAuthOptions, 'secret' | 'baseURL' | 'basePath'> & {
13
13
  /** The module registers its server handler at /api/auth. */
14
14
  basePath?: '/api/auth';
15
- plugins?: readonly BetterAuthPlugin[];
15
+ plugins?: readonly unknown[];
16
16
  };
17
17
  export type ClientAuthConfig = Omit<BetterAuthClientOptions, 'baseURL'> & {
18
18
  baseURL?: string;
@@ -108,8 +108,11 @@ export interface AuthRuntimeConfig {
108
108
  export interface AuthPrivateRuntimeConfig {
109
109
  hubSecondaryStorage: boolean | 'custom';
110
110
  }
111
- export declare function defineServerAuth<const R>(config: (ctx: ServerAuthContext) => R & ServerAuthConfig): (ctx: ServerAuthContext) => R;
112
- export declare function defineServerAuth<const R>(config: R & ServerAuthConfig & (R extends (...args: never[]) => unknown ? never : unknown)): (ctx: ServerAuthContext) => R;
111
+ export declare function defineServerAuth<const R extends ServerAuthConfig>(config: (ctx: ServerAuthContext) => R): (ctx: ServerAuthContext) => R;
112
+ export declare function defineServerAuth<const R>(config: (ctx: ServerAuthContext) => R & ServerAuthConfig & (R extends {
113
+ basePath: infer B;
114
+ } ? B extends '/api/auth' ? unknown : never : unknown)): (ctx: ServerAuthContext) => R;
115
+ export declare function defineServerAuth<const R extends ServerAuthConfig>(config: R & (R extends (...args: never[]) => unknown ? never : unknown)): (ctx: ServerAuthContext) => R;
113
116
  export declare function extendServerAuth<const R extends ServerAuthConfig, const P extends readonly BetterAuthPlugin[]>(createConfig: (ctx: ServerAuthContext) => R, plugins: P): (ctx: ServerAuthContext) => ExtendedServerAuthConfig<R, P>;
114
117
  export declare function defineClientAuth<T extends ClientAuthConfig>(config: T | ((ctx: ClientAuthContext) => T)): ClientAuthFactory<T>;
115
118
  export declare function extendClientAuth<T extends ClientAuthConfig, const P extends BetterAuthClientPlugin[]>(createClient: ClientAuthFactory<T>, plugins: P): ClientAuthFactory<ExtendedClientAuthConfig<T, P>>;
@@ -2,7 +2,9 @@ import { withoutBase } from "ufo";
2
2
  const internalAssetPaths = ["/_fonts", "/_ipx", "/_nuxt", "/api/_nuxt_icon"];
3
3
  export function isInternalAssetPath(path) {
4
4
  const pathname = path.split(/[?#]/, 1)[0] || "/";
5
- return internalAssetPaths.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
5
+ return internalAssetPaths.some((prefix) => {
6
+ return pathname === prefix || pathname.startsWith(`${prefix}/`) || pathname.endsWith(prefix) || pathname.includes(`${prefix}/`);
7
+ });
6
8
  }
7
9
  const internalRouteRuleAuthPaths = /* @__PURE__ */ new Set([
8
10
  "/__better-auth-devtools",
@@ -9,13 +9,18 @@ type UserAuthConfig = AuthOptions & {
9
9
  trustedOrigins?: BetterAuthOptions['trustedOrigins'];
10
10
  secondaryStorage?: BetterAuthOptions['secondaryStorage'];
11
11
  };
12
- type ResolvedAuthOptions = Omit<UserAuthConfig, 'secret' | 'baseURL' | 'trustedOrigins' | 'database'> & {
12
+ type ResolvedAuthOptions = Omit<UserAuthConfig, 'secret' | 'baseURL' | 'trustedOrigins' | 'database' | 'plugins'> & {
13
+ plugins?: BetterAuthOptions['plugins'];
13
14
  secret: string;
14
15
  baseURL: string;
15
16
  trustedOrigins?: BetterAuthOptions['trustedOrigins'];
16
17
  database?: BetterAuthOptions['database'];
17
18
  };
18
- type AuthInstance = ReturnType<typeof betterAuth<ResolvedAuthOptions>>;
19
+ type AuthInstance = ReturnType<typeof betterAuth<ResolvedAuthOptions>> & {
20
+ api: ReturnType<typeof betterAuth<ResolvedAuthOptions>>['api'] & {
21
+ signInUsername?: (...args: unknown[]) => unknown;
22
+ };
23
+ };
19
24
  /**
20
25
  * Returns the Better Auth instance like `serverAuth()`, after waiting for the auth secret that
21
26
  * Nuxt 4.6+ derives from `appSecret` (`NUXT_APP_SECRET`). Use it in code that can run before the
@@ -274,6 +274,7 @@ export function serverAuth(event) {
274
274
  }
275
275
  const authOptions = {
276
276
  ...userConfig,
277
+ ...userConfig.plugins ? { plugins: [...userConfig.plugins] } : {},
277
278
  ...rateLimit ? { rateLimit } : {},
278
279
  ...database ? { database } : {},
279
280
  secret: betterAuthSecret,
@@ -7,6 +7,8 @@ export interface AuthUser {
7
7
  emailVerified: boolean;
8
8
  name: string;
9
9
  image?: string | null;
10
+ /** Whether the user has enabled two-factor authentication. */
11
+ twoFactorEnabled?: boolean;
10
12
  }
11
13
  export interface AuthSession {
12
14
  id: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
3
  "type": "module",
4
- "version": "0.3.10",
4
+ "version": "0.3.11",
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",
@@ -94,7 +94,7 @@
94
94
  "@libsql/client": "^0.18.0",
95
95
  "@libsql/linux-x64-gnu": "0.5.29",
96
96
  "@nuxt/devtools": "^3.4.1",
97
- "@nuxt/fonts": "0.14.0",
97
+ "@nuxt/fonts": "1.0.1",
98
98
  "@nuxt/module-builder": "^1.0.3",
99
99
  "@nuxt/schema": "^4.6.0",
100
100
  "@nuxt/test-utils": "^4.3.3",
@@ -102,8 +102,8 @@
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.7",
106
- "better-call": "1.4.0",
105
+ "better-auth": "1.7.10",
106
+ "better-call": "1.4.2",
107
107
  "better-sqlite3": "^13.0.0",
108
108
  "drizzle-kit": "^0.31.10",
109
109
  "drizzle-orm": "^0.45.2",
@@ -112,15 +112,15 @@
112
112
  "nitropack": "2.13.4",
113
113
  "nuxt": "^4.6.0",
114
114
  "pinia": "^4.0.3",
115
- "pkg-pr-new": "0.0.88",
116
- "playwright-core": "1.63.0",
115
+ "pkg-pr-new": "0.0.91",
116
+ "playwright-core": "1.64.1",
117
117
  "postgres": "^3.4.9",
118
118
  "typescript": "~5.9.3",
119
119
  "unstorage": "^1.17.5",
120
120
  "vitest": "^5.0.0",
121
- "vue": "3.5.43",
121
+ "vue": "3.5.44",
122
122
  "vue-tsc": "^3.3.10",
123
- "wrangler": "4.147.0",
123
+ "wrangler": "4.151.1",
124
124
  "yaml": "^2.9.0"
125
125
  },
126
126
  "scripts": {