Skip to content

Commit bcbf1a3

Browse files
fredericDelaporteatinuxautofix-ci[bot]
authored
feat: add client-only load strategy (#443)
Co-authored-by: Sébastien Chopin <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Sébastien Chopin <[email protected]>
1 parent ac6a891 commit bcbf1a3

File tree

11 files changed

+2350
-6879
lines changed

11 files changed

+2350
-6879
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,18 @@ This is because the user session is stored in a secure cookie and cannot be acce
600600

601601
**This means that you should not rely on the user session during prerendering.**
602602

603+
You may also choose to instruct Nuxt AUth Utils to fetch the user session only on the client side, with the `loadStrategy` option in your `nuxt.config.ts`:
604+
605+
```ts
606+
export default defineNuxtConfig({
607+
auth: {
608+
loadStrategy: 'client-only'
609+
}
610+
})
611+
```
612+
613+
When using the `client-only` load strategy, the user session can still be manually fetched on the server side by calling `fetch` from the `useUserSession` composable.
614+
603615
### `<AuthState>` component
604616

605617
You can use the `<AuthState>` component to safely display auth-related data in your components without worrying about the rendering mode.
@@ -617,7 +629,7 @@ One common use case if the Login button in the header:
617629
</template>
618630
```
619631

620-
If the page is cached or prerendered, nothing will be rendered until the user session is fetched on the client-side.
632+
If the page is cached or prerendered or the load strategy set as `client-only`, nothing will be rendered until the user session is fetched on the client-side.
621633

622634
You can use the `placeholder` slot to show a placeholder on server-side and while the user session is being fetched on client-side for the prerendered pages:
623635

playground/nuxt.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export default defineNuxtConfig({
22
// ssr: false,
33
extends: ['@nuxt/ui-pro'],
4-
modules: ['nuxt-auth-utils', '@nuxt/ui', '@vueuse/nuxt'],
4+
modules: ['../src/module', '@nuxt/ui', '@vueuse/nuxt'],
55
imports: {
66
autoImport: true,
77
},
@@ -26,6 +26,7 @@ export default defineNuxtConfig({
2626
auth: {
2727
webAuthn: true,
2828
atproto: true,
29+
// loadStrategy: 'client-only'
2930
},
3031
icon: {
3132
customCollections: [{

playground/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,5 @@
1717
"nuxt": "^3.15.4",
1818
"nuxt-auth-utils": "latest",
1919
"zod": "^3.24.1"
20-
},
21-
"devDependencies": {
22-
"better-sqlite3": "^11.8.1"
2320
}
2421
}

pnpm-lock.yaml

Lines changed: 2299 additions & 6863 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
packages:
2-
- "playground"
3-
- "./"
2+
- ./
3+
- ./playground

src/module.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ export interface ModuleOptions {
3939
*/
4040
scrypt?: ScryptConfig
4141
}
42+
/**
43+
* Session load strategy
44+
* @default 'server-first'
45+
*/
46+
loadStrategy?: 'server-first' | 'client-only'
4247
}
4348

4449
declare module 'nuxt/schema' {
@@ -51,6 +56,12 @@ declare module 'nuxt/schema' {
5156
*/
5257
session: SessionConfig
5358
}
59+
60+
interface PublicRuntimeConfig {
61+
auth: {
62+
loadStrategy: 'server-first' | 'client-only'
63+
}
64+
}
5465
}
5566

5667
export default defineNuxtModule<ModuleOptions>({
@@ -65,6 +76,7 @@ export default defineNuxtModule<ModuleOptions>({
6576
hash: {
6677
scrypt: {},
6778
},
79+
loadStrategy: 'server-first',
6880
},
6981
async setup(options, nuxt) {
7082
const resolver = createResolver(import.meta.url)
@@ -122,8 +134,11 @@ export default defineNuxtModule<ModuleOptions>({
122134
})
123135
// Set node:crypto as unenv external
124136
nuxt.options.nitro.unenv ||= {}
137+
// @ts-expect-error we can use external as array
125138
nuxt.options.nitro.unenv.external ||= []
139+
// @ts-expect-error see comment above
126140
if (!nuxt.options.nitro.unenv.external.includes('node:crypto')) {
141+
// @ts-expect-error see comment above
127142
nuxt.options.nitro.unenv.external.push('node:crypto')
128143
}
129144

@@ -162,6 +177,11 @@ export default defineNuxtModule<ModuleOptions>({
162177
}
163178
}
164179

180+
// Load strategy
181+
runtimeConfig.public.auth = defu(runtimeConfig.public.auth, {
182+
loadStrategy: options.loadStrategy ?? 'server-first',
183+
})
184+
165185
// WebAuthn settings
166186
runtimeConfig.webauthn = defu(runtimeConfig.webauthn, {
167187
register: {},

src/runtime/app/plugins/session.client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ export default defineNuxtPlugin(async (nuxtApp) => {
66
if (!nuxtApp.payload.serverRendered) {
77
await useUserSession().fetch()
88
}
9-
else if (Boolean(nuxtApp.payload.prerenderedAt) || Boolean(nuxtApp.payload.isCached)) {
9+
else if (Boolean(nuxtApp.payload.prerenderedAt) || Boolean(nuxtApp.payload.isCached)
10+
|| nuxtApp.$config.public.auth.loadStrategy === 'client-only'
11+
) {
1012
// To avoid hydration mismatch
1113
nuxtApp.hook('app:mounted', async () => {
1214
await useUserSession().fetch()

src/runtime/app/plugins/session.server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ export default defineNuxtPlugin({
88
async setup(nuxtApp) {
99
// Flag if request is cached
1010
nuxtApp.payload.isCached = Boolean(useRequestEvent()?.context.cache)
11-
if (nuxtApp.payload.serverRendered && !nuxtApp.payload.prerenderedAt && !nuxtApp.payload.isCached) {
11+
if (nuxtApp.payload.serverRendered && !nuxtApp.payload.prerenderedAt && !nuxtApp.payload.isCached
12+
&& nuxtApp.$config.public.auth.loadStrategy !== 'client-only'
13+
) {
1214
await useUserSession().fetch()
1315
}
1416
},

src/runtime/server/lib/oauth/authentik.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export interface OAuthAuthentikConfig {
2727
* Redirect URL to allow overriding for situations like prod failing to determine public hostname
2828
* @default process.env.NUXT_OAUTH_AUTHENTIK_REDIRECT_URL or current URL
2929
*/
30-
redirectURL?: string,
30+
redirectURL?: string
3131

3232
/**
3333
* Authentik Scope

src/runtime/server/utils/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export async function getUserSession(event: UseSessionEvent): Promise<UserSessio
3131
const session = await _useSession(event)
3232
return {
3333
...session.data,
34-
id: session.id,
34+
id: session.id!,
3535
}
3636
}
3737
/**

0 commit comments

Comments
 (0)