@strivacity/sdk-nuxt 4.0.0-beta.0 → 4.0.0-beta.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.
Files changed (124) hide show
  1. package/README.md +171 -101
  2. package/dist/module.mjs +5 -4
  3. package/package.json +2 -2
  4. package/project.json +3 -0
  5. package/src/module.ts +7 -3
  6. package/src/runtime/composables/use-native-login.ts +27 -159
  7. package/src/runtime/composables/use-strivacity.ts +217 -67
  8. package/src/runtime/middleware/auth.server.ts +10 -1
  9. package/src/runtime/plugins/strivacity.client.ts +17 -0
  10. package/src/runtime/server/plugins/auth.server.ts +66 -30
  11. package/src/runtime/storages/default.ts +22 -67
  12. package/src/runtime/types.ts +7 -36
  13. package/src/runtime/utils/core.ts +2 -0
  14. package/testing/stubs/app.ts +8 -0
  15. package/testing/tests/composables.spec.ts +105 -4
  16. package/testing/tests/module.spec.ts +46 -0
  17. package/testing/tests/server/plugin.spec.ts +48 -10
  18. package/testing/tests/server/routes.spec.ts +2 -2
  19. package/.nuxt/app-component.mjs +0 -1
  20. package/.nuxt/app.config.mjs +0 -21
  21. package/.nuxt/component-chunk.mjs +0 -1
  22. package/.nuxt/component-names.mjs +0 -1
  23. package/.nuxt/components.d.ts +0 -66
  24. package/.nuxt/components.islands.mjs +0 -8
  25. package/.nuxt/components.plugin.mjs +0 -5
  26. package/.nuxt/css.mjs +0 -0
  27. package/.nuxt/error-component.mjs +0 -1
  28. package/.nuxt/fetch.d.ts +0 -1
  29. package/.nuxt/fetch.mjs +0 -8
  30. package/.nuxt/global-polyfills.mjs +0 -4
  31. package/.nuxt/imports.d.ts +0 -41
  32. package/.nuxt/imports.mjs +0 -42
  33. package/.nuxt/island-renderer.mjs +0 -2
  34. package/.nuxt/layouts.mjs +0 -2
  35. package/.nuxt/manifest/meta/cb11df97-b0c9-4a91-9d4b-60c25c918599.json +0 -1
  36. package/.nuxt/middleware.mjs +0 -7
  37. package/.nuxt/nitro.client.mjs +0 -2
  38. package/.nuxt/nuxt.config.mjs +0 -103
  39. package/.nuxt/nuxt.d.ts +0 -19
  40. package/.nuxt/nuxt.node.d.ts +0 -13
  41. package/.nuxt/nuxt.shared.d.ts +0 -7
  42. package/.nuxt/pages.mjs +0 -2
  43. package/.nuxt/paths.mjs +0 -15
  44. package/.nuxt/plugins.client.mjs +0 -14
  45. package/.nuxt/plugins.server.mjs +0 -10
  46. package/.nuxt/root-component.mjs +0 -1
  47. package/.nuxt/route-rules.mjs +0 -22
  48. package/.nuxt/router.options.mjs +0 -2
  49. package/.nuxt/schema/nuxt.schema.d.ts +0 -17
  50. package/.nuxt/schema/nuxt.schema.json +0 -3
  51. package/.nuxt/test-component-wrapper.mjs +0 -1
  52. package/.nuxt/tsconfig.app.json +0 -211
  53. package/.nuxt/tsconfig.json +0 -212
  54. package/.nuxt/tsconfig.node.json +0 -117
  55. package/.nuxt/tsconfig.server.json +0 -161
  56. package/.nuxt/tsconfig.shared.json +0 -165
  57. package/.nuxt/types/app.config.d.ts +0 -35
  58. package/.nuxt/types/build.d.ts +0 -0
  59. package/.nuxt/types/builder-env.d.ts +0 -1
  60. package/.nuxt/types/components.d.ts +0 -71
  61. package/.nuxt/types/imports.d.ts +0 -2
  62. package/.nuxt/types/layouts.d.ts +0 -16
  63. package/.nuxt/types/middleware.d.ts +0 -11
  64. package/.nuxt/types/modules.d.ts +0 -59
  65. package/.nuxt/types/nitro-config.d.ts +0 -14
  66. package/.nuxt/types/nitro-imports.d.ts +0 -20
  67. package/.nuxt/types/nitro-layouts.d.ts +0 -17
  68. package/.nuxt/types/nitro-nuxt.d.ts +0 -75
  69. package/.nuxt/types/nitro-routes.d.ts +0 -38
  70. package/.nuxt/types/nitro.d.ts +0 -3
  71. package/.nuxt/types/plugins.d.ts +0 -30
  72. package/.nuxt/types/runtime-config.d.ts +0 -38
  73. package/.nuxt/types/shared-app.config.d.ts +0 -28
  74. package/.nuxt/types/shared-imports.d.ts +0 -2
  75. package/.nuxt/types/vue-shim.d.ts +0 -0
  76. package/.nuxt/unhead-options.mjs +0 -5
  77. package/.nuxt/unhead.config.mjs +0 -1
  78. package/dist/module.d.mts +0 -7
  79. package/dist/module.json +0 -9
  80. package/dist/module.mjs.map +0 -1
  81. package/dist/runtime/composables/use-native-login-context.d.ts +0 -10
  82. package/dist/runtime/composables/use-native-login-context.js +0 -9
  83. package/dist/runtime/composables/use-native-login.d.ts +0 -10
  84. package/dist/runtime/composables/use-native-login.js +0 -143
  85. package/dist/runtime/composables/use-session.d.ts +0 -8
  86. package/dist/runtime/composables/use-session.js +0 -2
  87. package/dist/runtime/composables/use-strivacity.d.ts +0 -9
  88. package/dist/runtime/composables/use-strivacity.js +0 -77
  89. package/dist/runtime/middleware/auth.server.d.ts +0 -2
  90. package/dist/runtime/middleware/auth.server.js +0 -12
  91. package/dist/runtime/server/api/auth/backchannel-logout.get.d.ts +0 -2
  92. package/dist/runtime/server/api/auth/backchannel-logout.get.js +0 -2
  93. package/dist/runtime/server/api/auth/callback.get.d.ts +0 -2
  94. package/dist/runtime/server/api/auth/callback.get.js +0 -2
  95. package/dist/runtime/server/api/auth/entry.get.d.ts +0 -2
  96. package/dist/runtime/server/api/auth/entry.get.js +0 -2
  97. package/dist/runtime/server/api/auth/login.get.d.ts +0 -2
  98. package/dist/runtime/server/api/auth/login.get.js +0 -2
  99. package/dist/runtime/server/api/auth/logout.get.d.ts +0 -2
  100. package/dist/runtime/server/api/auth/logout.get.js +0 -2
  101. package/dist/runtime/server/api/auth/refresh.get.d.ts +0 -2
  102. package/dist/runtime/server/api/auth/refresh.get.js +0 -2
  103. package/dist/runtime/server/api/auth/register.get.d.ts +0 -2
  104. package/dist/runtime/server/api/auth/register.get.js +0 -2
  105. package/dist/runtime/server/api/auth/revoke.get.d.ts +0 -2
  106. package/dist/runtime/server/api/auth/revoke.get.js +0 -2
  107. package/dist/runtime/server/composables/use-strivacity.d.ts +0 -2
  108. package/dist/runtime/server/composables/use-strivacity.js +0 -9
  109. package/dist/runtime/server/plugins/auth.server.d.ts +0 -10
  110. package/dist/runtime/server/plugins/auth.server.js +0 -66
  111. package/dist/runtime/server/utils/helpers.d.ts +0 -18
  112. package/dist/runtime/server/utils/helpers.js +0 -39
  113. package/dist/runtime/storages/core.d.ts +0 -1
  114. package/dist/runtime/storages/core.js +0 -13
  115. package/dist/runtime/storages/default.d.ts +0 -21
  116. package/dist/runtime/storages/default.js +0 -71
  117. package/dist/runtime/types.d.ts +0 -219
  118. package/dist/runtime/types.js +0 -1
  119. package/dist/runtime/utils/core.d.ts +0 -1
  120. package/dist/runtime/utils/core.js +0 -64
  121. package/dist/runtime/utils/noop.d.ts +0 -1
  122. package/dist/runtime/utils/noop.js +0 -3
  123. package/dist/types.d.mts +0 -9
  124. /package/src/runtime/server/api/auth/{backchannel-logout.get.ts → backchannel-logout.post.ts} +0 -0
package/README.md CHANGED
@@ -111,7 +111,7 @@ export default defineNuxtConfig({
111
111
  redirectUri: 'https://your-app.example.com/callback', // callback URL after authentication
112
112
  scopes: ['openid', 'profile', 'email'], // requested user permissions/data
113
113
 
114
- serverSessionUri: '/auth/login', // Omit this line entirely for client-managed sessions
114
+ serverSessionUri: '/auth/login', // Set to `null` for client-managed sessions - omitting this line still defaults to server-managed
115
115
  secret: process.env.SECRET, // http-only cookie encryption key (random 32+ characters), required for server-managed sessions
116
116
  postLoginRedirectUri: '/profile',
117
117
  },
@@ -121,7 +121,7 @@ export default defineNuxtConfig({
121
121
  The module automatically:
122
122
  - registers `/auth/login`, `/auth/register`, `/auth/callback`, `/auth/refresh`, `/auth/revoke`, `/auth/entry`, `/auth/logout`, and `/auth/backchannel-logout` as Nitro server routes (see the table below)
123
123
  - auto-imports `useStrivacity()`/`useNativeLogin()` on the client and `useStrivacity(event)` on the server - no import statements needed anywhere
124
- - registers a global route middleware that resolves the session server-side before every render, so there's no separate provider component or session-prop wiring to do yourself
124
+ - with server-managed sessions, registers a global route middleware that resolves the session server-side before every render, so there's no separate provider component or session-prop wiring to do yourself. When the access token is within `refreshSkew` seconds of expiry and the session has a refresh token, the middleware refreshes it first (with the Server SDK's `getSession()`) and writes the rotated session cookie to the response; if the refresh fails, `useSession()` is `undefined`. Set `autoRefresh: false` in the Server SDK options to turn this off. The refresh token and ID token are removed (with `toClientSession()`) before the session is stored in `useSession()` (and serialized into the rendered HTML) - they stay in the server-side session
125
125
 
126
126
  | Method | Path | Description | Response |
127
127
  | ------ | --------------------------- | ---------------------------------------------------------- | -------------------------------------------- |
@@ -129,10 +129,10 @@ The module automatically:
129
129
  | `GET` | `/auth/register` | Starts the registration flow and redirects to the IDP | `302` redirect to IDP |
130
130
  | `GET` | `/auth/callback` | Completes authentication (handles the IDP callback) | `302` redirect or popup close script |
131
131
  | `GET` | `/auth/refresh` | Refreshes the access token | `204 No Content` or `302` redirect |
132
- | `GET` | `/auth/revoke` | Revokes tokens and clears the session | `204 No Content` |
133
- | `GET` | `/auth/logout` | Ends the session and redirects to the IDP logout page | `302` redirect to IDP logout |
134
- | `GET` | `/auth/entry` | Handles external flow entry (e.g., password reset link) - embedded/native modes only | JSON with session data |
135
- | `POST` | `/auth/backchannel-logout` | Processes back-channel logout requests from the IDP | `204 No Content` |
132
+ | `GET` | `/auth/revoke` | Revokes tokens and clears the session | `302` redirect to `postLogoutRedirectUri` |
133
+ | `GET` | `/auth/logout` | Ends the session and redirects to the IDP logout page; accepts a same-origin `?returnTo=` (default: `postLogoutRedirectUri`) | `302` to IDP logout (straight to `returnTo` when the session has no ID token) |
134
+ | `GET` | `/auth/entry` | Handles external flow entry (e.g., password reset link) - embedded/native modes only | `302` redirect to `loginUri` |
135
+ | `POST` | `/auth/backchannel-logout` | Processes back-channel logout requests from the IDP | `200 OK` (`400` invalid token, `501` storage without `deleteByLogoutToken`, `500` delete failed) |
136
136
 
137
137
  > The `/auth` prefix and route names come from `authUrlPrefix` - see [Server configuration reference](#server-configuration-reference).
138
138
 
@@ -233,7 +233,7 @@ Forward the callback query string to `/auth/callback` - the Server SDK completes
233
233
  <script setup lang="ts">
234
234
  const route = useRoute();
235
235
 
236
- await navigateTo({ path: '/auth/callback', query: route.query }, { redirectCode: 301, external: true });
236
+ await navigateTo({ path: '/auth/callback', query: route.query }, { redirectCode: 302, external: true });
237
237
  </script>
238
238
  ```
239
239
 
@@ -300,7 +300,7 @@ onMounted(() => {
300
300
 
301
301
  **Server-managed sessions**:
302
302
 
303
- With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
303
+ With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
304
304
 
305
305
  ```vue
306
306
  <!-- app/pages/logout.vue -->
@@ -342,7 +342,7 @@ async function onRevoke() {
342
342
 
343
343
  **Server-managed sessions**:
344
344
 
345
- With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back:
345
+ With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
346
346
 
347
347
  ```vue
348
348
  <script setup lang="ts">
@@ -404,20 +404,23 @@ onMounted(async () => {
404
404
  top: 100,
405
405
  toolbar: false,
406
406
  location: false,
407
- resizable: true,
408
- scrollbars: true,
409
- },
410
- })
411
- .then(() => navigate('/profile'))
412
- .catch((error) => navigate(`/error?message=${encodeURIComponent(error.message)}`));
413
- });
407
+ resizable: true,
408
+ scrollbars: true,
409
+ },
410
+ });
414
411
 
415
- return (
416
- <section>
417
- <h1>Opening login popup...</h1>
418
- </section>
419
- );
420
- }
412
+ await router.push('/profile');
413
+ } catch (error) {
414
+ await router.push(`/error?message=${encodeURIComponent(error instanceof Error ? error.message : 'Unknown error')}`);
415
+ }
416
+ });
417
+ </script>
418
+
419
+ <template>
420
+ <section>
421
+ <h1>Opening login popup...</h1>
422
+ </section>
423
+ </template>
421
424
  ```
422
425
 
423
426
  **Server-managed sessions**:
@@ -428,11 +431,30 @@ Popup mode always needs client-side JavaScript to open the window, so there's no
428
431
 
429
432
  **Client-managed sessions**:
430
433
 
431
- The popup resolves automatically - no callback route is needed. Token exchange happens inside the popup and the result is posted back to the opener window.
434
+ The pop-up still navigates to your `redirectUri`, so you still need a callback route there - without it nothing posts the result back to the opener. Call `handleCallback()` there; in popup mode it never resolves (the opener closes the pop-up once it receives the result), so don't rely on code after it running:
435
+
436
+ ```vue
437
+ <!-- app/pages/callback.vue -->
438
+ <script setup lang="ts">
439
+ const { handleCallback } = useStrivacity();
440
+
441
+ onMounted(() => {
442
+ void handleCallback();
443
+ });
444
+ </script>
445
+
446
+ <template>
447
+ <section>
448
+ <h1>Logging in...</h1>
449
+ </section>
450
+ </template>
451
+ ```
452
+
453
+ `login()` resolves in the opener once the result has been received and, for client-managed sessions, the tokens have been exchanged.
432
454
 
433
455
  **Server-managed sessions**:
434
456
 
435
- Same as client managed - the popup's internal callback request is also transparently proxied through `/auth/callback`, and the result is posted back to the opener window exactly the same way.
457
+ The callback page must not call `handleCallback()`: forward the query string to `/auth/callback` instead, as in the server-managed redirect callback. The Server SDK exchanges the code, sets the session cookie and responds with a page that posts the result to the opener and closes the pop-up.
436
458
 
437
459
  ##### Registration
438
460
 
@@ -510,7 +532,7 @@ onMounted(() => {
510
532
 
511
533
  **Server-managed sessions**:
512
534
 
513
- With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
535
+ With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
514
536
 
515
537
  ```vue
516
538
  <!-- app/pages/logout.vue -->
@@ -551,7 +573,7 @@ async function onRevoke() {
551
573
 
552
574
  **Server-managed sessions**:
553
575
 
554
- With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back:
576
+ With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
555
577
 
556
578
  ```vue
557
579
  <script setup lang="ts">
@@ -974,7 +996,7 @@ onMounted(() => {
974
996
 
975
997
  **Server-managed sessions**:
976
998
 
977
- With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
999
+ With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
978
1000
 
979
1001
  ```vue
980
1002
  <!-- app/pages/logout.vue -->
@@ -1015,7 +1037,7 @@ async function onRevoke() {
1015
1037
 
1016
1038
  **Server-managed sessions**:
1017
1039
 
1018
- With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back:
1040
+ With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
1019
1041
 
1020
1042
  ```vue
1021
1043
  <script setup lang="ts">
@@ -1064,7 +1086,6 @@ const { state, forms, messages, loading, submitForm, setFormValue } = useNativeL
1064
1086
  params: {
1065
1087
  prompt: 'login', // use 'create' to open the registration flow instead
1066
1088
  language: 'en-US', // set the UI language (BCP 47 language tag)
1067
- sdk: 'web-minimal', // rendering mode: 'web-minimal' for simplified rendering, 'web' (default) for full rendering hints and branding
1068
1089
  sessionId: searchParams.get('session_id'), // pass a session ID to resume an existing flow
1069
1090
  },
1070
1091
  onLogin: async () => {
@@ -1270,7 +1291,7 @@ onMounted(() => {
1270
1291
 
1271
1292
  **Server-managed sessions**:
1272
1293
 
1273
- With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
1294
+ With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
1274
1295
 
1275
1296
  ```vue
1276
1297
  <!-- app/pages/logout.vue -->
@@ -1311,7 +1332,7 @@ async function onRevoke() {
1311
1332
 
1312
1333
  **Server-managed sessions**:
1313
1334
 
1314
- With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back:
1335
+ With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
1315
1336
 
1316
1337
  ```vue
1317
1338
  <script setup lang="ts">
@@ -1367,17 +1388,36 @@ const ctx = useStrivacity<RedirectFlow>();
1367
1388
  accessTokenExpired: Ref<boolean>; // True once the access token has expired
1368
1389
  accessTokenExpirationDate: Ref<number | null>; // Access token expiration timestamp
1369
1390
 
1370
- // Methods (all are async)
1371
- login(params?: LoginParams): Promise<void>; // Start login flow
1372
- register(params?: LoginParams): Promise<void>; // Start registration flow
1373
- handleCallback(url?: string): Promise<void>; // Handle OAuth callback
1391
+ // Methods (proxied to `sdk`; parameter/return types follow the flow type)
1392
+ init(): Promise<void>; // Load metadata and restore any persisted session
1393
+ checkAuthentication(params?: { autoRefresh: boolean }): Promise<boolean>; // Check the session, optionally refreshing tokens (never when serverSessionUri is set)
1394
+ login(params?: RedirectParams | PopupParams): Promise<void>; // Start login flow (redirect: `returnTo` sets where handleCallback() sends the user) (embedded/native: deprecated, returns a login flow instance - use `sdk.startSession()`)
1395
+ register(params?: RedirectParams | PopupParams): Promise<void>; // Start registration flow (same caveat as `login`)
1396
+ handleCallback(url?: string | URL): Promise<{ returnTo?: string }>; // Handle OAuth callback; resolves to the `returnTo` given to login()
1374
1397
  logout(params?: LogoutParams): Promise<void>; // End session
1375
- refresh(): Promise<void>; // Refresh access token
1398
+ refresh(params?: { returnTo?: string }): Promise<void>; // Refresh access token (server-managed: navigates to the server's refresh route, `${authUrlPrefix}/refresh`, instead; `returnTo` sets where it returns, default: the current URL)
1376
1399
  revoke(): Promise<void>; // Revoke tokens
1377
- entry(): Promise<EntryData>; // Handle external entry (embedded/native only)
1400
+ tokenExchange(params?: Record<string, string>): Promise<void>; // Perform a token exchange
1401
+ entry(url?: string | URL): Promise<EntryResponse>; // Handle external entry (embedded/native; resolves `{ session_id, short_app_id, language }`). Redirect/popup: `entry(url?, params?: RedirectOptions): Promise<void>`
1402
+ subscribeToEvent(eventName, callbackFn): { dispose: () => void }; // Subscribe to a single SDK event
1403
+ subscribeToAllEvents(callbackFn): { dispose: () => void }; // Subscribe to all SDK events
1378
1404
  }
1379
1405
  ```
1380
1406
 
1407
+ ##### During server-side rendering
1408
+
1409
+ The SDK instance only exists in the browser: a client-only plugin creates it when the app starts, and it is also available as `useNuxtApp().$strivacity`. During SSR, `useStrivacity()` returns a **read-only** context built from the current request's session (`useSession()`), so no state is ever shared between requests:
1410
+
1411
+ | Member | During SSR |
1412
+ | --- | --- |
1413
+ | `isAuthenticated`, `idTokenClaims`, `accessToken`, `accessTokenExpired`, `accessTokenExpirationDate` | Derived from `useSession()`. With client-managed sessions the server cannot see browser storage, so `isAuthenticated` is `false` |
1414
+ | `loading` | `false` with server-managed sessions, `true` with client-managed sessions (until the browser takes over) |
1415
+ | `refreshToken` | Always `null` |
1416
+ | `sdk.options`, `sdk.session` | Readable |
1417
+ | `checkAuthentication()`, `init()` | Resolve immediately |
1418
+ | `subscribeToEvent()`, `subscribeToAllEvents()` | No-ops (no events fire on the server) |
1419
+ | `login()`, `register()`, `logout()`, `refresh()`, `revoke()`, `handleCallback()`, `entry()`, `tokenExchange()` and the matching `sdk.*` methods | Throw: authentication flows only run in the browser. Call them from `onMounted()` or event handlers |
1420
+
1381
1421
  #### useNativeLogin
1382
1422
 
1383
1423
  Composable for managing native login flow state. Only available in `native` mode.
@@ -1399,10 +1439,10 @@ const ctx = useNativeLogin({
1399
1439
  {
1400
1440
  params?: NativeParams; // Initial flow parameters
1401
1441
  onLogin?: (session: SessionData) => void | Promise<void>; // Called on successful login
1402
- onClose?: () => void; // Called when user closes the flow
1403
- onError?: (error: unknown) => void; // Called on error
1404
- onFallback?: (error: FallbackError) => void; // Called when fallback needed
1405
- onGlobalMessage?: (message: NativeFlowMessage) => void; // Called for global messages
1442
+ onClose?: () => void | Promise<void>; // Called when user closes the flow
1443
+ onError?: (error: Error) => void | Promise<void>; // Called on error
1444
+ onFallback?: (error: FallbackError) => void | Promise<void>; // Called when fallback needed
1445
+ onGlobalMessage?: (message: NativeFlowMessage) => void | Promise<void>; // Called for global messages
1406
1446
  }
1407
1447
  ```
1408
1448
 
@@ -1455,10 +1495,12 @@ export default defineEventHandler(async (event) => {
1455
1495
  throw createError({ statusCode: 401 });
1456
1496
  }
1457
1497
 
1458
- return { idTokenClaims: session.idTokenClaims };
1498
+ return { idTokenClaims: session.claims };
1459
1499
  });
1460
1500
  ```
1461
1501
 
1502
+ `sdk.getSession()` returns what the page-render middleware gets: with `autoRefresh` (the default) it refreshes an expired (or within `refreshSkew`) session first when a refresh token exists, at most once per request (later calls in the same request share the result), and resolves to `null` if the refresh fails. The default cookie storage writes the rotated cookie to the response directly; a custom storage that queues its `Set-Cookie` headers on the event needs them flushed (`flushSetCookies`). If you return the session itself to the browser, strip the refresh and ID tokens with `toClientSession(session)` (from `@strivacity/sdk-nuxt/utils`).
1503
+
1462
1504
  > `useStrivacity(event)` throws if called on the client or without an `H3Event` - it's only available inside server routes, server plugins, and Nitro middleware.
1463
1505
 
1464
1506
  <a id="server-storages"></a>
@@ -1469,7 +1511,7 @@ By default the Server SDK stores tokens encrypted in http-only cookies and login
1469
1511
  #### Built-in session storages
1470
1512
 
1471
1513
  - **`createEncryptedCookieStorage(secret, options?)`** - default storage that keeps the session encrypted in an http-only cookie.
1472
- - **`createSessionIdCookieStorage(storage, options?)`** - puts only a small, random session-id cookie on the client and keeps the actual session payload in the `storage` you provide. This supports back-channel logout out of the box.
1514
+ - **`createSessionIdCookieStorage(storage, options?)`** - puts only a small, random session-id cookie on the client and keeps the actual session payload in the `storage` you provide. It supports back-channel logout when `storage` implements `deleteByLogoutToken`.
1473
1515
 
1474
1516
  ```ts
1475
1517
  // server/storage.ts
@@ -1491,50 +1533,57 @@ export default defineNuxtConfig({
1491
1533
  modules: ['@strivacity/sdk-nuxt'],
1492
1534
  strivacity: {
1493
1535
  // ...other options
1494
- storageFactoryPath: '~/server/storage', // default-exports the factory shown above
1536
+ storageFactoryPath: '~~/server/storage', // default-exports the factory shown above
1495
1537
  },
1496
1538
  });
1497
1539
  ```
1498
1540
 
1499
1541
  #### Custom storage
1500
1542
 
1501
- For example you can use Redis via [unstorage](https://npmjs.com/package/unstorage):
1543
+ For example you can use Redis via [unstorage](https://npmjs.com/package/unstorage).
1544
+
1545
+ The SDK always reads and writes the session under the same key (`storageTokenName`), and passes the current H3 event as the last argument. A storage that ignores the event would therefore share a single session across all users, so key the data per browser session instead. The easiest way is to wrap your key-value store with `createSessionIdCookieStorage`, which puts a random session id in a cookie and calls your store with that id as the key:
1502
1546
 
1503
1547
  ```ts
1504
1548
  // server/storage.ts
1505
1549
  import { createStorage } from 'unstorage';
1506
1550
  import redisDriver from 'unstorage/drivers/redis';
1507
- import type { NuxtServerStorage } from '@strivacity/sdk-nuxt';
1551
+ import type { ServerStorage } from '@strivacity/sdk-nuxt';
1552
+ import { createSessionIdCookieStorage } from '#imports';
1508
1553
 
1509
1554
  const unstorageInstance = createStorage({ driver: redisDriver({ url: process.env.REDIS_URL }) });
1510
1555
 
1511
- export default function createStorageFactory(): NuxtServerStorage {
1512
- return {
1513
- async get(key) {
1514
- return unstorageInstance.getItem<string>(key);
1515
- },
1516
- async set(key, value) {
1517
- await unstorageInstance.setItem(key, value);
1518
- },
1519
- async delete(key) {
1520
- await unstorageInstance.removeItem(key);
1521
- },
1522
- // Required for back-channel logout support - see below.
1523
- // Scans all stored sessions and removes those matching the logout token's sid or sub claim.
1524
- async deleteByLogoutToken(logoutToken) {
1525
- const keys = await unstorageInstance.getKeys();
1526
- await Promise.all(
1527
- keys.map(async (key) => {
1528
- const raw = await unstorageInstance.getItem<string>(key);
1529
- if (!raw) return;
1530
- const session = JSON.parse(raw);
1531
- if ((logoutToken.sid && session.sid === logoutToken.sid) || (logoutToken.sub && session.sub === logoutToken.sub)) {
1532
- await unstorageInstance.removeItem(key);
1533
- }
1534
- }),
1535
- );
1536
- },
1537
- };
1556
+ // Session payload store - `key` is the per-browser session id provided by `createSessionIdCookieStorage`
1557
+ // (raw get/set keep the serialized session string as-is)
1558
+ const sessionStore: ServerStorage = {
1559
+ async get(key) {
1560
+ return unstorageInstance.getItemRaw<string>(`session:${key}`);
1561
+ },
1562
+ async set(key, value) {
1563
+ await unstorageInstance.setItemRaw(`session:${key}`, value);
1564
+ },
1565
+ async delete(key) {
1566
+ await unstorageInstance.removeItem(`session:${key}`);
1567
+ },
1568
+ // Required for back-channel logout support - see below.
1569
+ // Scans all stored sessions and removes those whose ID token claims match the logout token's sid or sub claim.
1570
+ async deleteByLogoutToken(logoutToken) {
1571
+ const keys = await unstorageInstance.getKeys('session');
1572
+ await Promise.all(
1573
+ keys.map(async (key) => {
1574
+ const raw = await unstorageInstance.getItemRaw<string>(key);
1575
+ if (!raw) return;
1576
+ const session = JSON.parse(raw);
1577
+ if ((logoutToken.sid && session.claims?.sid === logoutToken.sid) || (logoutToken.sub && session.claims?.sub === logoutToken.sub)) {
1578
+ await unstorageInstance.removeItem(key);
1579
+ }
1580
+ }),
1581
+ );
1582
+ },
1583
+ };
1584
+
1585
+ export default function createStorageFactory() {
1586
+ return createSessionIdCookieStorage(sessionStore); // Custom Redis-backed session storage, keyed by a session-id cookie
1538
1587
  }
1539
1588
  ```
1540
1589
 
@@ -1544,7 +1593,7 @@ export default defineNuxtConfig({
1544
1593
  modules: ['@strivacity/sdk-nuxt'],
1545
1594
  strivacity: {
1546
1595
  // ...other options
1547
- storageFactoryPath: '~/server/storage', // default-exports the factory shown above
1596
+ storageFactoryPath: '~~/server/storage', // default-exports the factory shown above
1548
1597
  },
1549
1598
  });
1550
1599
  ```
@@ -1555,9 +1604,9 @@ export default defineNuxtConfig({
1555
1604
 
1556
1605
  OIDC back-channel logout lets the authorization server terminate sessions server-to-server, without involving the browser. When the IDP sends a logout event (e.g. an admin terminates a session, or the user logs out from a different device), it POSTs a signed `logout_token` JWT to `/auth/backchannel-logout` - already registered by the module - which routes it to `sdk.handleBackChannelLogout(event)`.
1557
1606
 
1558
- The handler verifies the token's signature against the IDP's JWKS, validates the `iss`, `aud`, `iat` (freshness), and `jti` (replay protection) claims, requires the `http://schemas.openid.net/event/backchannel-logout` event and a `sid` or `sub` claim, then calls `storage.deleteByLogoutToken({ sid?, sub? })` to remove the matching session(s). It responds `200` on success, `400` for an invalid or malformed `logout_token`, and `501` if the configured storage doesn't implement `deleteByLogoutToken`.
1607
+ The handler verifies the token's signature against the IDP's JWKS, validates the `iss`, `aud`, `iat` (freshness), and `jti` (replay protection) claims, requires the `http://schemas.openid.net/event/backchannel-logout` event and a `sid` or `sub` claim, then calls `storage.deleteByLogoutToken({ sid?, sub? })` to remove the matching session(s). It responds `200` on success, `400` for an invalid or malformed `logout_token`, `501` if the configured storage doesn't implement `deleteByLogoutToken`, and `500` if deleting the session fails.
1559
1608
 
1560
- > **The default encrypted-cookie storage does not support back-channel logout** because each cookie is bound to a single browser session - there is no server-side index to look up by `sid` or `sub`. To support back-channel logout, use [`createSessionIdCookieStorage`](#server-storages) with a `storage` that implements `deleteByLogoutToken` (e.g. `createServerMemoryStorage()` for local testing), or a fully custom server storage as shown in the [Custom storage](#server-storages) example above.
1609
+ > **The default encrypted-cookie storage does not support back-channel logout** because each cookie is bound to a single browser session - there is no server-side index to look up by `sid` or `sub`. To support back-channel logout, use [`createSessionIdCookieStorage`](#server-storages) with a `storage` that implements `deleteByLogoutToken`, e.g. `createServerMemoryStorage()` for local testing or the Redis-backed store from the [Custom storage](#server-storages) example above.
1561
1610
 
1562
1611
  Configure the **Back-channel logout URI** in your Strivacity application settings to:
1563
1612
 
@@ -1576,7 +1625,7 @@ For a complete explanation of the handshake and validation performed, see the co
1576
1625
  options: NuxtServerSDKOptions; // resolved server SDK configuration
1577
1626
 
1578
1627
  // Session management
1579
- getSession(): Promise<SessionData | null>; // read the current session
1628
+ getSession(): Promise<SessionData | null>; // read the current session; with `autoRefresh` (default) it refreshes an expired (or within `refreshSkew`) one first, once per request; null if the refresh fails (what the SSR middleware uses)
1580
1629
  updateSession(session): Promise<void>; // persist new session data
1581
1630
  refreshSession(): Promise<SessionData>; // refresh tokens using the refresh token
1582
1631
  revokeSession(): Promise<void>; // revoke tokens and clear the session
@@ -1584,20 +1633,20 @@ For a complete explanation of the handshake and validation performed, see the co
1584
1633
  completeLogin(params): Promise<SessionData>; // exchange an authorization code for tokens
1585
1634
  logout(postLogoutRedirectUri): Promise<URL>; // clear the session, returns the IDP end-session URL
1586
1635
 
1587
- // Route handlers - each returns a Response; `handler` dispatches to the one matching the request path
1588
- handleLogin(): Promise<Response>;
1589
- handleRegister(): Promise<Response>;
1590
- handleCallback(): Promise<Response>;
1591
- handleRefresh(): Promise<Response>;
1592
- handleRevoke(): Promise<Response>;
1593
- handleEntry(): Promise<Response>;
1594
- handleLogout(): Promise<Response>;
1595
- handleBackChannelLogout(): Promise<Response>;
1596
- handler(): Promise<Response>;
1636
+ // Route handlers - each takes the `H3Event` and returns a Response; `handler` dispatches to the one matching the request path
1637
+ handleLogin(event): Promise<Response>;
1638
+ handleRegister(event): Promise<Response>;
1639
+ handleCallback(event): Promise<Response>;
1640
+ handleRefresh(event): Promise<Response>;
1641
+ handleRevoke(event): Promise<Response>;
1642
+ handleEntry(event): Promise<Response>;
1643
+ handleLogout(event): Promise<Response>;
1644
+ handleBackChannelLogout(event): Promise<Response>;
1645
+ handler(event): Promise<Response | null>; // null if no auth route matched
1597
1646
  }
1598
1647
  ```
1599
1648
 
1600
- All methods already close over the current `H3Event` - there's no `event` parameter to pass at the call site.
1649
+ The session-management methods already close over the current `H3Event` - there's no `event` parameter to pass at the call site. The route handlers (`handle*()` and `handler()`) take the event explicitly, e.g. `await sdk.handler(event)`.
1601
1650
 
1602
1651
  ### Server configuration reference
1603
1652
 
@@ -1610,27 +1659,44 @@ The Server SDK accepts the same configuration as the client SDK (see [Configurat
1610
1659
  | `stateStorageFactoryPath` | `string` | No | In-memory `Map` | Path to a module default-exporting an `SDKStorage` factory for the OAuth2 state parameter |
1611
1660
  | `loggingFactoryPath` | `string` | No | - | Path to a module default-exporting an `SDKLogging` factory |
1612
1661
  | `httpClientFactoryPath` | `string` | No | - | Path to a module default-exporting an `SDKHttpClient` factory |
1613
- | `authUrlPrefix` | `string` | No | `'/auth'` | URL prefix under which the module registers the auth routes |
1662
+ | `authUrlPrefix` | `string` | No | `'/auth'` | URL prefix under which the module registers the auth routes; with server-managed sessions `sdk.refresh()` and route guards send an expired session to `${authUrlPrefix}/refresh` |
1614
1663
  | `serverSessionUri` | `string \| null` | No | `` `${authUrlPrefix}/login` `` | Enables server-managed sessions when set (the default); set to `null` to use client-managed sessions instead |
1615
- | `postLoginRedirectUri` | `string` | No | - | Default redirect after login when no `?returnTo=` is given |
1616
- | `postLogoutRedirectUri` | `string` | No | - | Default redirect after logout |
1664
+ | `postLoginRedirectUri` | `string` | No | origin of `redirectUri` | Default redirect after login when no `?returnTo=` is given |
1665
+ | `postLogoutRedirectUri` | `string` | No | origin of `redirectUri` | Default redirect after logout |
1617
1666
  | `cookieMaxAge` | `number` | No | `2592000` (30 days) | Max age of the session cookie in seconds |
1618
1667
 
1619
1668
  ---
1620
1669
 
1621
1670
  ### Route guards
1622
1671
 
1623
- There's no client-side route guard composable in this SDK. Instead, add an opt-in Nuxt route middleware and apply it per-page:
1672
+ There's no client-side route guard composable in this SDK. Instead, add an opt-in Nuxt route middleware and apply it per-page.
1673
+
1674
+ With server-managed sessions the browser never refreshes tokens itself, and the module's middleware only refreshes during SSR, so a session can expire between client-side navigations. The guard below sends an expired session through the Server SDK's refresh route (`sdk.refresh({ returnTo })`, a full-page navigation that comes back to the target page) and aborts the client-side navigation:
1624
1675
 
1625
1676
  ```ts
1626
1677
  // app/middleware/auth.ts
1627
1678
  export default defineNuxtRouteMiddleware(async (to) => {
1628
- const sdk = useStrivacity();
1629
- const authenticated = sdk.options.serverSessionUri ? !!useSession().value : await sdk.isAuthenticated;
1679
+ const { sdk } = useStrivacity();
1630
1680
 
1631
- if (!authenticated) {
1632
- const returnToCookie = useCookie('sty.returnTo');
1633
- returnToCookie.value = to.fullPath;
1681
+ // With client-managed sessions the session lives in browser storage, so only the browser can decide.
1682
+ if (import.meta.server && !sdk.options.serverSessionUri) {
1683
+ return;
1684
+ }
1685
+
1686
+ // Server-managed sessions, client-side navigation: send an expired session through the server's refresh route.
1687
+ // During SSR the module middleware already refreshed it.
1688
+ const session = useSession().value;
1689
+
1690
+ if (import.meta.client && sdk.options.serverSessionUri && session && isSessionExpired(session)) {
1691
+ await sdk.refresh({ returnTo: to.fullPath });
1692
+ return abortNavigation();
1693
+ }
1694
+
1695
+ const isAuthenticated = sdk.options.serverSessionUri ? !!useSession().value : await sdk.isAuthenticated;
1696
+
1697
+ if (!isAuthenticated) {
1698
+ const returnToCookie = useCookie('sty.returnTo', { sameSite: 'lax', path: '/' });
1699
+ returnToCookie.value = to.path;
1634
1700
 
1635
1701
  return navigateTo('/login');
1636
1702
  }
@@ -1644,7 +1710,7 @@ definePageMeta({ middleware: ['auth'] });
1644
1710
  </script>
1645
1711
  ```
1646
1712
 
1647
- This is separate from the global session-hydration middleware the module registers automatically (see [Quick start](#quick-start)) - that one always runs and only seeds `useSession()`, it never redirects. The guard above is what actually protects a page, and you choose which pages need it.
1713
+ This is separate from the global session-hydration middleware the module registers with server-managed sessions (see [Quick start](#quick-start)) - that one runs during SSR only and only seeds `useSession()` (refreshing a near-expiry session first, see [Quick start](#quick-start)), it never redirects. A failed refresh leaves `useSession()` `undefined`, so the guard above sends the user to log in. The guard above is what actually protects a page, and you choose which pages need it. To protect every page instead, name it `auth.global.ts` (and skip `/login` and other public routes inside it): the module registers its own middleware first, so `useSession()` is already filled when any app middleware runs, global ones included.
1648
1714
 
1649
1715
  ---
1650
1716
 
@@ -1672,7 +1738,11 @@ The client SDK accepts the same configuration as the core SDK - see the [core SD
1672
1738
 
1673
1739
  ### Migrating to v4.0
1674
1740
 
1675
- v4 replaces the SDK's class-based flow architecture with function-based architecture, and unifies server-managed (BFF) session handling onto a shared Server SDK. `useStrivacity()`, `useNativeLogin()`, and the `strivacity: {...}` module configuration are unchanged - only apps that used `mode: 'custom'` or drove `native` mode through the old `NativeFlowHandler` need to update their code.
1741
+ v4 replaces the SDK's class-based flow architecture with function-based architecture, and unifies server-managed (BFF) session handling onto a shared Server SDK. The `strivacity: {...}` key is still the only configuration, but `serverSessionUri` now defaults to server-managed sessions: add a `secret`, or set `serverSessionUri: null` to keep v3's client-managed behaviour. Custom storage and logging are referenced by file path (`storageFactoryPath`, `loggingFactoryPath`, ...). Apps need to update their code if they rendered `<StyLoginRenderer>` (replaced by `useNativeLogin()`), used `mode: 'custom'`, drove `native` mode through the old `NativeFlowHandler`, or call SDK methods during server-side rendering.
1742
+
1743
+ #### `useStrivacity()` is read-only during SSR
1744
+
1745
+ In v3 the SDK instance was a module-level singleton that was also created on the server, so concurrent requests shared it. In v4 the SDK instance only exists in the browser, and during SSR `useStrivacity()` returns a read-only context derived from the current request's session (see [During server-side rendering](#during-server-side-rendering)). Move calls such as `login()`, `logout()` or `handleCallback()` into `onMounted()` or event handlers.
1676
1746
 
1677
1747
  #### Class-based flows replaced by functions
1678
1748
 
@@ -1699,7 +1769,7 @@ import type { SDKInitConfig, SDKOptions } from '@strivacity/sdk-core/types';
1699
1769
 
1700
1770
  export function createCustomFlow(initConfig: SDKInitConfig) {
1701
1771
  const state = getDefaultFlowState();
1702
- const options = getSDKOptions<SDKOptions>(state, initConfig);
1772
+ const options = getSDKOptions<SDKOptions>(initConfig);
1703
1773
  const base = createBaseFlow(state, options);
1704
1774
 
1705
1775
  async function refresh(): Promise<void> {
@@ -1722,7 +1792,7 @@ export default defineNuxtConfig({
1722
1792
  modules: ['@strivacity/sdk-nuxt'],
1723
1793
  strivacity: {
1724
1794
  // ...
1725
- serverSessionUri: '/auth/login', // omit this line entirely for client-managed sessions
1795
+ serverSessionUri: '/auth/login', // set to `null` for client-managed sessions - omitting this line still defaults to server-managed
1726
1796
  secret: process.env.SECRET,
1727
1797
  },
1728
1798
  });
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, resolvePath, addImportsDir, addServerImportsDir, addServerPlugin, addRouteMiddleware, addServerHandler } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, resolvePath, addPlugin, addImportsDir, addServerImportsDir, addServerPlugin, addRouteMiddleware, addServerHandler } from '@nuxt/kit';
2
2
 
3
3
  const module$1 = defineNuxtModule({
4
4
  meta: {
@@ -31,6 +31,7 @@ const module$1 = defineNuxtModule({
31
31
  nuxt.options.nitro.alias["#strivacity-options-httpClient"] = httpClientPath;
32
32
  nuxt.options.nitro.alias["#strivacity-options-storage"] = storagePath;
33
33
  nuxt.options.nitro.alias["#strivacity-options-stateStorage"] = stateStoragePath;
34
+ addPlugin({ src: resolver.resolve("./runtime/plugins/strivacity.client"), mode: "client" });
34
35
  addImportsDir(resolver.resolve("./runtime/composables"));
35
36
  addImportsDir(resolver.resolve("./runtime/storages"));
36
37
  addImportsDir(resolver.resolve("./runtime/utils"));
@@ -39,7 +40,7 @@ const module$1 = defineNuxtModule({
39
40
  addServerImportsDir(resolver.resolve("./runtime/server/utils"));
40
41
  addServerImportsDir(resolver.resolve("./runtime/storages"));
41
42
  addServerPlugin(resolver.resolve("./runtime/server/plugins/auth.server"));
42
- addRouteMiddleware({ name: "strivacity", path: resolver.resolve("./runtime/middleware/auth.server"), global: true });
43
+ addRouteMiddleware({ name: "strivacity", path: resolver.resolve("./runtime/middleware/auth.server"), global: true }, { prepend: true });
43
44
  addServerHandler({
44
45
  method: "get",
45
46
  route: `${options.authUrlPrefix}/login`,
@@ -76,9 +77,9 @@ const module$1 = defineNuxtModule({
76
77
  handler: resolver.resolve("./runtime/server/api/auth/logout.get")
77
78
  });
78
79
  addServerHandler({
79
- method: "get",
80
+ method: "post",
80
81
  route: `${options.authUrlPrefix}/backchannel-logout`,
81
- handler: resolver.resolve("./runtime/server/api/auth/backchannel-logout.get")
82
+ handler: resolver.resolve("./runtime/server/api/auth/backchannel-logout.post")
82
83
  });
83
84
  }
84
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strivacity/sdk-nuxt",
3
- "version": "4.0.0-beta.0",
3
+ "version": "4.0.0-beta.2",
4
4
  "description": "Strivacity Nuxt SDK client",
5
5
  "license": "MIT",
6
6
  "author": "strivacity <opensource@strivacity.com>",
@@ -42,7 +42,7 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@strivacity/sdk-core": "4.0.0-beta.0"
45
+ "@strivacity/sdk-core": "4.0.0-beta.2"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "nuxt": ">=4.0"
package/project.json CHANGED
@@ -20,6 +20,9 @@
20
20
  "parallel": false
21
21
  }
22
22
  },
23
+ "eslint": {
24
+ "dependsOn": ["^build", "prepare"]
25
+ },
23
26
  "build": {
24
27
  "executor": "nx:run-commands",
25
28
  "dependsOn": ["sdk-core:build", "prepare"],
package/src/module.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  addServerHandler,
9
9
  addServerPlugin,
10
10
  addRouteMiddleware,
11
+ addPlugin,
11
12
  } from '@nuxt/kit';
12
13
 
13
14
  export type * from './runtime/types';
@@ -50,6 +51,7 @@ export default defineNuxtModule<NuxtServerSDKInitConfig>({
50
51
  nuxt.options.nitro.alias['#strivacity-options-storage'] = storagePath;
51
52
  nuxt.options.nitro.alias['#strivacity-options-stateStorage'] = stateStoragePath;
52
53
 
54
+ addPlugin({ src: resolver.resolve('./runtime/plugins/strivacity.client'), mode: 'client' });
53
55
  addImportsDir(resolver.resolve('./runtime/composables'));
54
56
  addImportsDir(resolver.resolve('./runtime/storages'));
55
57
  addImportsDir(resolver.resolve('./runtime/utils'));
@@ -60,7 +62,9 @@ export default defineNuxtModule<NuxtServerSDKInitConfig>({
60
62
  addServerImportsDir(resolver.resolve('./runtime/storages'));
61
63
 
62
64
  addServerPlugin(resolver.resolve('./runtime/server/plugins/auth.server'));
63
- addRouteMiddleware({ name: 'strivacity', path: resolver.resolve('./runtime/middleware/auth.server'), global: true });
65
+ // NOTE: prepend, so the session is loaded (and refreshed) before any app middleware runs - including global ones such as
66
+ // `app/middleware/auth.global.ts`, which Nuxt otherwise runs before module-registered middleware.
67
+ addRouteMiddleware({ name: 'strivacity', path: resolver.resolve('./runtime/middleware/auth.server'), global: true }, { prepend: true });
64
68
 
65
69
  addServerHandler({
66
70
  method: 'get',
@@ -98,9 +102,9 @@ export default defineNuxtModule<NuxtServerSDKInitConfig>({
98
102
  handler: resolver.resolve('./runtime/server/api/auth/logout.get'),
99
103
  });
100
104
  addServerHandler({
101
- method: 'get',
105
+ method: 'post',
102
106
  route: `${options.authUrlPrefix}/backchannel-logout`,
103
- handler: resolver.resolve('./runtime/server/api/auth/backchannel-logout.get'),
107
+ handler: resolver.resolve('./runtime/server/api/auth/backchannel-logout.post'),
104
108
  });
105
109
  }
106
110
  },