@lenne.tech/nuxt-extensions 1.11.1 → 1.11.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.
package/dist/module.d.mts CHANGED
@@ -3,7 +3,7 @@ import { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
3
3
  export * from '../dist/runtime/types/index.js';
4
4
 
5
5
  declare const name = "@lenne.tech/nuxt-extensions";
6
- declare const version = "1.11.1";
6
+ declare const version = "1.11.2";
7
7
  declare const configKey = "ltExtensions";
8
8
  declare const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
9
9
  declare const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "configKey": "ltExtensions",
3
3
  "name": "@lenne.tech/nuxt-extensions",
4
- "version": "1.11.1",
4
+ "version": "1.11.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineNuxtModule, createResolver, tryResolveModule, addImports, addComponent, addPlugin, addRouteMiddleware } from '@nuxt/kit';
2
2
 
3
3
  const name = "@lenne.tech/nuxt-extensions";
4
- const version = "1.11.1";
4
+ const version = "1.11.2";
5
5
  const configKey = "ltExtensions";
6
6
  const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
7
7
  const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
@@ -110,7 +110,7 @@ export function useLtAuth() {
110
110
  if (!isJwtMode.value || !jwtToken.value) return false;
111
111
  return switchToJwtMode();
112
112
  }
113
- const PATHS_REQUIRING_COOKIES = ["/passkey/", "/two-factor/", "/2fa/"];
113
+ const PATHS_REQUIRING_COOKIES = ["/passkey/", "/two-factor/", "/2fa/", "/get-session"];
114
114
  function urlRequiresCookies(url) {
115
115
  return PATHS_REQUIRING_COOKIES.some((path) => url.includes(path));
116
116
  }
@@ -39,7 +39,8 @@ export function getOrCreateLtAuthClient(config) {
39
39
  }
40
40
  export function createLtAuthClient(config = {}) {
41
41
  const useProxy = isLocalDevApiProxy();
42
- const defaultBaseURL = useProxy ? "" : import.meta.env?.VITE_API_URL || process.env.API_URL || "";
42
+ const envApiUrl = typeof import.meta.env?.VITE_API_URL === "string" ? import.meta.env.VITE_API_URL : void 0;
43
+ const defaultBaseURL = useProxy ? "" : envApiUrl || process.env.API_URL || "";
43
44
  const defaultBasePath = useProxy ? "/api/iam" : "/iam";
44
45
  const {
45
46
  baseURL = defaultBaseURL,
@@ -29,7 +29,17 @@ export default (nuxtApp) => {
29
29
  "/forgot-password",
30
30
  "/reset-password",
31
31
  "/verify-email",
32
- "/session",
32
+ // Better Auth's session routes, spelled out. A generic '/session' entry
33
+ // used to stand here and never matched anything: the routes are
34
+ // `get-session`, `list-sessions`, `revoke-session`, … — in every one of
35
+ // them the character before `session` is `-`, not `/`, so `url.includes`
36
+ // returned false. `/get-session` matters most: it is the URL the session
37
+ // probe below calls, and exempting it is what keeps a 401 from the probe
38
+ // out of this handler.
39
+ "/get-session",
40
+ "/list-sessions",
41
+ "/revoke-session",
42
+ "/revoke-other-sessions",
33
43
  "/token",
34
44
  // Passkey endpoints - handled by authFetch with JWT fallback
35
45
  "/passkey/",
@@ -43,19 +53,38 @@ export default (nuxtApp) => {
43
53
  ];
44
54
  return authEndpoints.some((endpoint) => url.includes(endpoint));
45
55
  }
56
+ const ALIVE_VERDICT_TTL_MS = 1e3;
57
+ let aliveVerdictValidUntil = 0;
46
58
  async function isSessionStillAlive() {
47
59
  try {
48
- const { fetchWithAuth } = getAuth();
60
+ const { fetchWithAuth, isJwtMode, switchToJwtMode } = getAuth();
49
61
  const response = await fetchWithAuth(`${getLtApiBase()}/get-session`, { method: "GET" });
50
- if (!response.ok) {
62
+ if (response.status === 401 || response.status === 403) {
51
63
  return false;
52
64
  }
65
+ if (!response.ok) {
66
+ return null;
67
+ }
53
68
  const data = await response.json().catch(() => null);
54
- return Boolean(data && (data.user || data.session));
69
+ const alive = Boolean(data && (data.user || data.session));
70
+ if (alive && isJwtMode.value) {
71
+ await switchToJwtMode().catch(() => false);
72
+ }
73
+ return alive;
55
74
  } catch {
56
75
  return null;
57
76
  }
58
77
  }
78
+ async function probeSessionAlive() {
79
+ if (Date.now() < aliveVerdictValidUntil) {
80
+ return true;
81
+ }
82
+ const verdict = await isSessionStillAlive();
83
+ if (verdict === true) {
84
+ aliveVerdictValidUntil = Date.now() + ALIVE_VERDICT_TTL_MS;
85
+ }
86
+ return verdict;
87
+ }
59
88
  async function handleUnauthorized(requestUrl) {
60
89
  if (isHandling401) {
61
90
  return;
@@ -66,28 +95,35 @@ export default (nuxtApp) => {
66
95
  if (isPublicAuthRoute()) {
67
96
  return;
68
97
  }
98
+ const { clearUser, isAuthenticated } = getAuth();
99
+ if (!isAuthenticated.value) {
100
+ return;
101
+ }
69
102
  isHandling401 = true;
103
+ let loggingOut = false;
70
104
  try {
71
- const { clearUser, isAuthenticated } = getAuth();
72
- if (isAuthenticated.value) {
73
- const sessionAlive = await isSessionStillAlive();
74
- if (sessionAlive !== false) {
75
- console.debug(
76
- sessionAlive ? `[LtAuth Interceptor] 401 from ${requestUrl ?? "unknown URL"} but session is still valid \u2014 treating it as a permission error, not logging out` : "[LtAuth Interceptor] 401 received but session state could not be verified \u2014 not logging out"
77
- );
78
- return;
79
- }
80
- console.debug("[LtAuth Interceptor] Session expired, logging out...");
81
- clearUser();
82
- const router = nuxtApp.$router;
83
- const currentPath = router?.currentRoute?.value?.fullPath;
84
- const redirectQuery = currentPath && currentPath !== loginPath ? `?redirect=${encodeURIComponent(currentPath)}` : "";
85
- window.location.href = loginPath + redirectQuery;
105
+ const sessionAlive = await probeSessionAlive();
106
+ if (sessionAlive !== false) {
107
+ console.debug(
108
+ sessionAlive ? `[LtAuth Interceptor] 401 from ${requestUrl ?? "unknown URL"} but session is still valid \u2014 treating it as a permission error, not logging out` : "[LtAuth Interceptor] 401 received but session state could not be verified \u2014 not logging out"
109
+ );
110
+ return;
86
111
  }
112
+ console.debug("[LtAuth Interceptor] Session expired, logging out...");
113
+ loggingOut = true;
114
+ clearUser();
115
+ const router = nuxtApp.$router;
116
+ const currentPath = router?.currentRoute?.value?.fullPath;
117
+ const redirectQuery = currentPath && currentPath !== loginPath ? `?redirect=${encodeURIComponent(currentPath)}` : "";
118
+ window.location.href = loginPath + redirectQuery;
87
119
  } finally {
88
- setTimeout(() => {
120
+ if (loggingOut) {
121
+ setTimeout(() => {
122
+ isHandling401 = false;
123
+ }, 1e3);
124
+ } else {
89
125
  isHandling401 = false;
90
- }, 1e3);
126
+ }
91
127
  }
92
128
  }
93
129
  const wrapMarker = "__ltAuthFetchWrapped";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nuxt-extensions",
3
- "version": "1.11.1",
3
+ "version": "1.11.2",
4
4
  "description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -101,13 +101,13 @@
101
101
  "devDependencies": {
102
102
  "@better-auth/passkey": "1.6.23",
103
103
  "@nuxt/module-builder": "1.0.2",
104
- "@nuxt/schema": "4.4.8",
104
+ "@nuxt/schema": "4.5.2",
105
105
  "@playwright/test": "1.61.1",
106
106
  "@types/node": "26.1.1",
107
107
  "@vitest/coverage-v8": "4.1.10",
108
108
  "better-auth": "1.6.23",
109
109
  "happy-dom": "20.10.6",
110
- "nuxt": "4.4.8",
110
+ "nuxt": "4.5.2",
111
111
  "oxfmt": "0.59.0",
112
112
  "oxlint": "1.74.0",
113
113
  "tus-js-client": "4.3.1",