@nuxtjs/better-auth 0.3.3 → 0.3.5

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.3",
3
+ "version": "0.3.5",
4
4
  "configKey": "auth",
5
5
  "compatibility": {
6
6
  "nuxt": ">=4.0.0"
package/dist/module.mjs CHANGED
@@ -10,9 +10,10 @@ import { defu } from 'defu';
10
10
  import { toRouteMatcher, createRouter } from 'radix3';
11
11
  import { randomBytes } from 'node:crypto';
12
12
  import { isCI, isTest } from 'std-env';
13
+ import { isInternalAssetPath } from '../dist/runtime/internal/auth-route-rules.js';
13
14
  export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
14
15
 
15
- const version = "0.3.3";
16
+ const version = "0.3.5";
16
17
 
17
18
  const CONFIG_EXTENSIONS = [".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"];
18
19
  const CONFIG_EXTENSION_RE = /\.[cm]?[jt]s$/;
@@ -742,7 +743,8 @@ function setupRuntimeConfig(input) {
742
743
  login: options.redirects?.login ?? "/login",
743
744
  guest: options.redirects?.guest ?? "/",
744
745
  authenticated: options.redirects?.authenticated,
745
- logout: options.redirects?.logout
746
+ logout: options.redirects?.logout,
747
+ sessionExpired: options.redirects?.sessionExpired
746
748
  },
747
749
  preserveRedirect: options.preserveRedirect ?? true,
748
750
  redirectQueryKey: options.redirectQueryKey ?? "redirect",
@@ -990,6 +992,8 @@ function assertSafeAuthRouteRules(routeRules) {
990
992
  for (const path of collectRouteRulePaths(patterns, ""))
991
993
  paths.add(path);
992
994
  const conflicts = [...paths].flatMap((path) => {
995
+ if (isInternalAssetPath(path))
996
+ return [];
993
997
  const matches = matcher.matchAll(path);
994
998
  const effectiveRule = defu({}, ...matches.reverse());
995
999
  if (effectiveRule.auth === void 0 || effectiveRule.auth === false)
@@ -37,6 +37,7 @@ export function useUserSession() {
37
37
  const authReady = useState("auth:ready", () => false);
38
38
  const prerenderReadyResetQueued = useState("auth:prerender-ready-reset-queued", () => false);
39
39
  const hydrationReconcileQueued = useState("auth:hydration-reconcile-queued", () => false);
40
+ const signOutInProgress = useState("auth:sign-out-in-progress", () => false);
40
41
  const ready = computed(() => authReady.value);
41
42
  const loggedIn = computed(() => Boolean(session.value && user.value));
42
43
  const isPrerenderedPayload = computed(() => Boolean(nuxtApp.payload.prerenderedAt || nuxtApp.payload.isCached));
@@ -94,13 +95,30 @@ export function useUserSession() {
94
95
  }
95
96
  }
96
97
  let mountedReconciliationStarted = false;
98
+ let sessionExpiredRedirecting = false;
99
+ function getSessionExpiredRedirect() {
100
+ return runtimeConfig.public.auth?.redirects?.sessionExpired;
101
+ }
102
+ function scheduleSessionExpiredRedirect() {
103
+ const sessionExpiredRedirect = getSessionExpiredRedirect();
104
+ if (!sessionExpiredRedirect || signOutInProgress.value || sessionExpiredRedirecting)
105
+ return;
106
+ sessionExpiredRedirecting = true;
107
+ void nextTick().then(() => {
108
+ if (sessionExpiredRedirecting)
109
+ void navigateTo(sessionExpiredRedirect);
110
+ });
111
+ }
97
112
  function queueHydrationReconciliation() {
98
113
  if (hydrationReconcileQueued.value)
99
114
  return;
100
115
  hydrationReconcileQueued.value = true;
101
116
  const reconcile = async () => {
117
+ const sessionWasActive = loggedIn.value;
102
118
  try {
103
119
  await fetchSession({ force: true });
120
+ if (sessionWasActive && !loggedIn.value)
121
+ scheduleSessionExpiredRedirect();
104
122
  } catch (error) {
105
123
  console.error("[nuxt-better-auth] Failed to fetch session during hydration reconciliation:", error);
106
124
  } finally {
@@ -119,16 +137,19 @@ export function useUserSession() {
119
137
  if (runtimeFlags.client && rawClient && !_sessionSyncApps.has(nuxtApp)) {
120
138
  const clientSession = rawClient.useSession();
121
139
  const initialClientSession = clientSession.value;
140
+ let hydrationSessionWasActive = nuxtApp.isHydrating && nuxtApp.payload.serverRendered && Boolean(session.value && user.value) && !initialClientSession?.data?.session && !initialClientSession?.data?.user;
122
141
  const shouldReconcileInitialHydration = nuxtApp.isHydrating && nuxtApp.payload.serverRendered && Boolean(session.value && user.value) && !initialClientSession?.data?.session && !initialClientSession?.data?.user && !initialClientSession?.isPending && !initialClientSession?.isRefetching;
123
142
  if (shouldReconcileInitialHydration)
124
143
  queueHydrationReconciliation();
125
144
  watch(
126
145
  () => clientSession.value,
127
- (newSession) => {
146
+ (newSession, previousSession) => {
128
147
  const shouldWaitForPrerenderResolution = isPrerenderHydrationEmptySnapshot.value && !newSession?.data?.session && !newSession?.data?.user;
129
148
  if (shouldWaitForPrerenderResolution)
130
149
  return;
131
150
  if (newSession?.data?.session && newSession?.data?.user) {
151
+ sessionExpiredRedirecting = false;
152
+ hydrationSessionWasActive = false;
132
153
  session.value = stripToken(newSession.data.session);
133
154
  user.value = newSession.data.user;
134
155
  } else if (!newSession?.isPending && !newSession?.isRefetching) {
@@ -137,8 +158,14 @@ export function useUserSession() {
137
158
  queueHydrationReconciliation();
138
159
  return;
139
160
  }
140
- if (!newSession?.error || isExpectedSignedOutSessionError(newSession.error))
161
+ const sessionWasActive = hydrationSessionWasActive || Boolean(previousSession?.data?.session && previousSession?.data?.user);
162
+ const sessionWasInvalidated = !newSession?.error || isExpectedSignedOutSessionError(newSession.error);
163
+ if (sessionWasInvalidated) {
164
+ hydrationSessionWasActive = false;
141
165
  clearSession();
166
+ }
167
+ if (sessionWasActive && sessionWasInvalidated)
168
+ scheduleSessionExpiredRedirect();
142
169
  }
143
170
  if (!authReady.value && !newSession?.isPending && !newSession?.isRefetching)
144
171
  authReady.value = true;
@@ -172,19 +199,24 @@ export function useUserSession() {
172
199
  return;
173
200
  }
174
201
  _signOutPromise = (async () => {
175
- const result = await rawClient.signOut();
176
- if (isRecord(result) && result.error)
177
- throw new Error(normalizeAuthActionError(result.error).message);
178
- clearSession();
179
- if (options?.onSuccess) {
180
- await options.onSuccess();
181
- return;
182
- }
183
- const authConfig = runtimeConfig.public.auth;
184
- const logoutRedirect = authConfig?.redirects?.logout;
185
- if (logoutRedirect) {
186
- await nextTick();
187
- await navigateTo(logoutRedirect);
202
+ signOutInProgress.value = true;
203
+ try {
204
+ const result = await rawClient.signOut();
205
+ if (isRecord(result) && result.error)
206
+ throw new Error(normalizeAuthActionError(result.error).message);
207
+ clearSession();
208
+ if (options?.onSuccess) {
209
+ await options.onSuccess();
210
+ return;
211
+ }
212
+ const authConfig = runtimeConfig.public.auth;
213
+ const logoutRedirect = authConfig?.redirects?.logout;
214
+ if (logoutRedirect) {
215
+ await nextTick();
216
+ await navigateTo(logoutRedirect);
217
+ }
218
+ } finally {
219
+ signOutInProgress.value = false;
188
220
  }
189
221
  })().finally(() => {
190
222
  _signOutPromise = null;
@@ -58,6 +58,8 @@ export interface BetterAuthModuleOptions {
58
58
  authenticated?: string;
59
59
  /** Where to navigate after logout. Default: no automatic navigation */
60
60
  logout?: string;
61
+ /** Where to navigate when a previously authenticated session expires or is revoked. Default: no automatic navigation */
62
+ sessionExpired?: string;
61
63
  };
62
64
  /**
63
65
  * When redirecting unauthenticated users to the login route, append a query param
@@ -95,6 +97,7 @@ export interface AuthRuntimeConfig {
95
97
  guest: string;
96
98
  authenticated?: string;
97
99
  logout?: string;
100
+ sessionExpired?: string;
98
101
  };
99
102
  preserveRedirect: boolean;
100
103
  redirectQueryKey: string;
@@ -1,4 +1,5 @@
1
1
  import type { AuthRouteRules } from '../types.js';
2
+ export declare function isInternalAssetPath(path: string): boolean;
2
3
  export declare function normalizeAuthRoutePath(path: string, baseURL?: string): string;
3
4
  export declare function normalizeAuthRouteRule(rule: unknown): AuthRouteRules['auth'];
4
5
  export declare function shouldSkipAuthRouteRules(path: string): boolean;
@@ -1,22 +1,21 @@
1
1
  import { withoutBase } from "ufo";
2
+ const internalAssetPaths = ["/_fonts", "/_ipx", "/_nuxt", "/api/_nuxt_icon"];
3
+ export function isInternalAssetPath(path) {
4
+ const pathname = path.split(/[?#]/, 1)[0] || "/";
5
+ return internalAssetPaths.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
6
+ }
2
7
  const internalRouteRuleAuthPaths = /* @__PURE__ */ new Set([
3
- "/_ipx",
4
- "/_nuxt",
5
8
  "/__better-auth-devtools",
6
9
  "/__nuxt_devtools__",
7
10
  "/__nuxt_error",
8
11
  "/__nuxt_vite_node__",
9
12
  "/api/_better-auth",
10
- "/api/_nuxt_icon",
11
13
  "/api/auth"
12
14
  ]);
13
15
  const internalRouteRuleAuthPrefixes = [
14
- "/_ipx/",
15
- "/_nuxt/",
16
16
  "/__nuxt_devtools__/",
17
17
  "/__nuxt_vite_node__/",
18
18
  "/api/_better-auth/",
19
- "/api/_nuxt_icon/",
20
19
  "/api/auth/"
21
20
  ];
22
21
  export function normalizeAuthRoutePath(path, baseURL = "/") {
@@ -30,5 +29,5 @@ export function normalizeAuthRouteRule(rule) {
30
29
  }
31
30
  export function shouldSkipAuthRouteRules(path) {
32
31
  const pathname = path.split(/[?#]/, 1)[0] || "/";
33
- return internalRouteRuleAuthPaths.has(pathname) || internalRouteRuleAuthPrefixes.some((prefix) => pathname.startsWith(prefix));
32
+ return isInternalAssetPath(pathname) || internalRouteRuleAuthPaths.has(pathname) || internalRouteRuleAuthPrefixes.some((prefix) => pathname.startsWith(prefix));
34
33
  }
@@ -21,7 +21,8 @@ export default defineEventHandler(async (event) => {
21
21
  login: publicAuth?.redirects?.login ?? "/login",
22
22
  guest: publicAuth?.redirects?.guest ?? "/",
23
23
  authenticated: publicAuth?.redirects?.authenticated,
24
- logout: publicAuth?.redirects?.logout
24
+ logout: publicAuth?.redirects?.logout,
25
+ sessionExpired: publicAuth?.redirects?.sessionExpired
25
26
  },
26
27
  preserveRedirect: publicAuth?.preserveRedirect ?? true,
27
28
  redirectQueryKey: publicAuth?.redirectQueryKey ?? "redirect",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/better-auth",
3
3
  "type": "module",
4
- "version": "0.3.3",
4
+ "version": "0.3.5",
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,6 +94,7 @@
94
94
  "@libsql/client": "^0.17.4",
95
95
  "@libsql/linux-x64-gnu": "0.5.29",
96
96
  "@nuxt/devtools": "^3.4.1",
97
+ "@nuxt/fonts": "0.14.0",
97
98
  "@nuxt/module-builder": "^1.0.3",
98
99
  "@nuxt/schema": "^4.5.2",
99
100
  "@nuxt/test-utils": "^4.1.0",