Skip to content

Commit f30fdde

Browse files
authored
fix(auth): preserve nuxthub db package imports in prerender builds (#305)
1 parent 55f58a0 commit f30fdde

16 files changed

Lines changed: 347 additions & 257 deletions

File tree

docs/content/3.guides/9.production-deployment.md

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Checklist and best practices for deploying Nuxt Better Auth in prod
55

66
## Environment Variables
77

8-
Production requires these environment variables at runtime:
8+
Production requires these environment variables:
99

1010
```ini [.env.production]
1111
# Required: 32+ character secret for session encryption
@@ -25,7 +25,7 @@ GOOGLE_CLIENT_SECRET="..."
2525
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
2626
```
2727

28-
The secret must be at least 32 characters. Shorter secrets will cause auth initialization to throw an error at runtime.
28+
The secret must be at least 32 characters. Shorter secrets will cause the module to throw an error.
2929

3030
## Security Checklist
3131

@@ -79,10 +79,6 @@ export default defineEventHandler((event) => {
7979

8080
For production, consider using Cloudflare rate limiting or a Redis-backed solution.
8181

82-
### Separate Build And Runtime Environments
83-
84-
Some platforms, including Cloudflare deployments, expose build-time and runtime environment variables separately. `NUXT_BETTER_AUTH_SECRET` must be available to the deployed runtime, but it does not need to be present in the build container.
85-
8682
### Trusted Origins for Preview Environments
8783

8884
If your workflow uses preview URLs (for example, `*.workers.dev`), include those origins in your Better Auth server config.
@@ -121,11 +117,7 @@ export default defineNuxtConfig({
121117

122118
### "NUXT_BETTER_AUTH_SECRET must be at least 32 characters"
123119

124-
Your secret is too short. Generate a new one using the command above. This error is raised when auth initializes at runtime. `BETTER_AUTH_SECRET` is still accepted as a fallback, but `NUXT_BETTER_AUTH_SECRET` is the recommended variable.
125-
126-
### "NUXT_BETTER_AUTH_SECRET is required in production"
127-
128-
The deployed server runtime could not resolve an auth secret. Set `NUXT_BETTER_AUTH_SECRET` or `BETTER_AUTH_SECRET` in the runtime environment for your app.
120+
Your secret is too short. Generate a new one using the command above. `BETTER_AUTH_SECRET` is still accepted as a fallback, but `NUXT_BETTER_AUTH_SECRET` is the recommended variable.
129121

130122
### "siteUrl required in production"
131123

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
"eslint": "^10.0.3",
9393
"npm-agentskills": "https://pkg.pr.new/onmax/npm-agentskills@394499e",
9494
"nuxt": "^4.3.1",
95+
"postgres": "^3.4.9",
9596
"tinyexec": "^1.0.2",
9697
"typescript": "~5.9.3",
9798
"vitest": "^4.0.18",

pnpm-lock.yaml

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

src/module.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,21 @@ export { schema }
210210
runtimeTypesPath: resolver.resolve('./runtime/types'),
211211
})
212212

213-
if (hasHubDb)
213+
if (hasHubDb) {
214+
// Keep @nuxthub/db as a bare specifier during Nitro bundling so the
215+
// prerender entry doesn't rewrite it to a broken relative path like
216+
// `../../../../../../../../@nuxthub/db/db.mjs` when `.nuxt` lives
217+
// inside `node_modules/.cache/nuxt/`.
218+
// @ts-expect-error Nitro augments NuxtHooks at runtime.
219+
nuxt.hook('nitro:config', (nitroConfig: { externals?: { external?: string[] } }) => {
220+
nitroConfig.externals ||= {}
221+
nitroConfig.externals.external ||= []
222+
if (!nitroConfig.externals.external.includes('@nuxthub/db'))
223+
nitroConfig.externals.external.push('@nuxthub/db')
224+
})
225+
214226
await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola, options.hubSecondaryStorage ?? false)
227+
}
215228
}
216229

217230
registerSharedTypeTemplates({

src/module/runtime.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ export function setupRuntimeConfig(input: SetupRuntimeConfigInput): { useHubKV:
6868
const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret as string | undefined
6969
nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.NUXT_BETTER_AUTH_SECRET || process.env.BETTER_AUTH_SECRET || ''
7070

71+
const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret as string
72+
if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
73+
throw new Error('[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET is required in production. Set NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET environment variable.')
74+
}
75+
if (betterAuthSecret && betterAuthSecret.length < 32) {
76+
throw new Error('[nuxt-better-auth] NUXT_BETTER_AUTH_SECRET must be at least 32 characters for security')
77+
}
78+
7179
nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth as Record<string, unknown>, {
7280
hubSecondaryStorage: options.hubSecondaryStorage ?? false,
7381
}) as AuthPrivateRuntimeConfig

src/runtime/server/utils/auth.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { getRequestHost, getRequestProtocol } from 'h3'
1010
import { useRuntimeConfig } from 'nitropack/runtime'
1111
import { withoutProtocol } from 'ufo'
1212
import { resolveCustomSecondaryStorageRequirement } from './custom-secondary-storage'
13-
import { validateAuthSecret } from './validate-secret'
1413

1514
type AuthOptions = ReturnType<typeof createServerAuth>
1615
type AuthInstance = ReturnType<typeof betterAuth<AuthOptions>>
@@ -257,7 +256,6 @@ export function serverAuth(event?: H3Event): AuthInstance {
257256
if (cached)
258257
return cached
259258

260-
const betterAuthSecret = validateAuthSecret(runtimeConfig.betterAuthSecret)
261259
const database = createDatabase()
262260
const userConfig = createServerAuth({ runtimeConfig, db }) as BetterAuthOptions & {
263261
secondaryStorage?: BetterAuthOptions['secondaryStorage']
@@ -277,7 +275,7 @@ export function serverAuth(event?: H3Event): AuthInstance {
277275
...userConfig,
278276
...(database && { database }),
279277
...(hubSecondaryStorage === true && { secondaryStorage: createSecondaryStorage() }),
280-
secret: betterAuthSecret,
278+
secret: runtimeConfig.betterAuthSecret,
281279
baseURL: siteUrl,
282280
trustedOrigins,
283281
})

src/runtime/server/utils/validate-secret.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
setups.@onmax/nuxt-better-auth="0.0.2-alpha.31"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<NuxtPage />
3+
</template>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { defineClientAuth } from '../../../../src/runtime/config'
2+
3+
export default defineClientAuth({})

0 commit comments

Comments
 (0)