@merkaly/nuxt 0.10.2 → 0.10.4

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/README.md CHANGED
@@ -35,6 +35,19 @@ On boot the module restores an existing session (user and token) and then runs t
35
35
  | store | optional | required | `merkaly:tenant` resolves the store from the host (public); a signed-in buyer is restored too; protected pages (account, checkout) ask for login. |
36
36
  | www | optional | not used | No hooks; anonymous or signed-in. |
37
37
 
38
+ ### Session lifetime and Auth0 setup for a new app
39
+
40
+ The SPA keeps the session with **refresh tokens** (`useRefreshTokens`, cached in `localStorage`, login with `offline_access`). The access token for the API lasts **2 hours** (`token_lifetime_for_web` of `https://api.merkaly.io`); before each API call the module asks Auth0 for the current token, which is cached and renewed with the refresh token once it expires. When the refresh token is gone or expired, protected surfaces (`requiresAuth: true`) go back to login instead of calling the API without a token.
41
+
42
+ Every new Nuxt app needs this in Auth0 (done for admin, dashboard, store and www on 2026-11-04):
43
+
44
+ 1. **Application** (type SPA): allowed callback (`/auth`), logout and web origin URLs for each environment.
45
+ 2. **Refresh Token Rotation: on**, **Refresh Token Expiration: on**, with idle lifetime `2592000` (30 days) and maximum lifetime `7776000` (90 days). Never non-rotating or non-expiring.
46
+ 3. **API `https://api.merkaly.io`**: "Allow Offline Access" stays **on**; without it Auth0 issues no refresh tokens and every session ends after 2 hours.
47
+ 4. **Organizations**: apps that work inside a store (admin) require an organization at login; staff-only apps are filtered by the "Enforce surface access" post-login Action (role `MK_*`).
48
+
49
+ These settings can be read and changed through the Management API (`merkaly.us.auth0.com`, the canonical domain, never the custom `auth.merkaly.io`).
50
+
38
51
  ## SEO: robots and sitemap
39
52
 
40
53
  Every app gets `@nuxtjs/robots`. Apps are **private by default**: in production
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.14.0"
6
6
  },
7
- "version": "0.10.2",
7
+ "version": "0.10.4",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -130,6 +130,8 @@ function configureI18n(nuxt, options) {
130
130
  });
131
131
  });
132
132
  return {
133
+ baseUrl: options.seo?.url || void 0,
134
+ // absolute hreflang and canonical links
133
135
  defaultLocale: options.i18n.defaultLocale,
134
136
  detectBrowserLanguage: { useCookie: true },
135
137
  restructureDir: ".",
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { useRoute } from "#app";
3
3
  import { computed } from "vue";
4
- import { useAuth, useTenant, watchOnce, addRouteMiddleware, useNuxtApp } from "#imports";
4
+ import { useAuth, useTenant, watchOnce, addRouteMiddleware, useNuxtApp, useRuntimeConfig } from "#imports";
5
5
  import AuthMiddleware from "../middleware/auth";
6
6
  import { useNavigation } from "../composables/useNavigation";
7
7
  import AuthError from "./auth/AuthError.vue";
@@ -11,8 +11,10 @@ const $route = useRoute();
11
11
  const { clear, error, isLoading, logout, retry } = useAuth();
12
12
  const { fromServer } = useTenant();
13
13
  const { hook } = useNuxtApp();
14
+ const { public: { merkaly } } = useRuntimeConfig();
14
15
  useColorMode({ attribute: "data-bs-theme" });
15
- const blocking = computed(() => isLoading.value && !fromServer.value);
16
+ const waitsForAuth = merkaly.auth0.requiresAuth || merkaly.auth0.requiresTenant;
17
+ const blocking = computed(() => waitsForAuth && isLoading.value && !fromServer.value);
16
18
  watchOnce(isLoading, () => AuthMiddleware($route, $route));
17
19
  const { defer, regenerate } = useNavigation();
18
20
  addRouteMiddleware("navigation", (to) => defer(to), { global: true });
@@ -1,4 +1,4 @@
1
- import { defineNuxtPlugin, useRequestURL, useRuntimeConfig } from "#app";
1
+ import { defineNuxtPlugin, useNuxtApp, useRequestURL, useRuntimeConfig } from "#app";
2
2
  import { useAuth, useTenant } from "#imports";
3
3
  const GLOBAL_API_HEADER = "x-merkaly-global";
4
4
  function serverBaseURL(api, global) {
@@ -10,8 +10,10 @@ export default defineNuxtPlugin(({ provide }) => {
10
10
  if (import.meta.server && import.meta.dev && new URL(useRuntimeConfig().public.merkaly.api.url).hostname.endsWith(".test")) {
11
11
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
12
12
  }
13
+ const nuxtApp = useNuxtApp();
13
14
  provide("api", async (url, options = {}) => {
14
15
  const { token } = useAuth();
16
+ if (import.meta.client && token.value) await nuxtApp.$auth0?.getTokenSilently();
15
17
  const { tenant } = useTenant();
16
18
  const { public: { merkaly } } = useRuntimeConfig();
17
19
  const requiresTenant = options.requiresTenant ?? merkaly.auth0.requiresTenant;
@@ -43,6 +43,9 @@ export default defineNuxtPlugin(async ({ callHook, hook }) => {
43
43
  if (user.value && !token.value && tokenError) {
44
44
  user.value = void 0;
45
45
  await self0.logout({ openUrl: false });
46
+ if ($config.merkaly.auth0.requiresAuth) {
47
+ return auth0.loginWithRedirect({ appState: { target: location.pathname + location.search } });
48
+ }
46
49
  }
47
50
  return initTenantContext();
48
51
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merkaly/nuxt",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/merkaly-io/nuxt.git"