@onmax/nuxt-better-auth 0.0.2-alpha.13 → 0.0.2-alpha.15

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.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: nuxt-better-auth
3
+ description: Use when implementing auth in Nuxt apps with @onmax/nuxt-better-auth - provides useUserSession composable, server auth helpers, route protection, and Better Auth plugins integration.
4
+ license: MIT
5
+ ---
6
+
7
+ # Nuxt Better Auth
8
+
9
+ Authentication module for Nuxt 4+ built on [Better Auth](https://www.better-auth.com/). Provides composables, server utilities, and route protection.
10
+
11
+ > **Alpha Status**: This module is currently in alpha (v0.0.2-alpha.12) and not recommended for production use. APIs may change.
12
+
13
+ ## When to Use
14
+
15
+ - Installing/configuring `@onmax/nuxt-better-auth`
16
+ - Implementing login/signup/signout flows
17
+ - Protecting routes (client and server)
18
+ - Accessing user session in API routes
19
+ - Integrating Better Auth plugins (admin, passkey, 2FA)
20
+ - Setting up database with NuxtHub
21
+ - Using clientOnly mode for external auth backends
22
+
23
+ **For Nuxt patterns:** use `nuxt` skill
24
+ **For NuxtHub database:** use `nuxthub` skill
25
+
26
+ ## Available Guidance
27
+
28
+ | File | Topics |
29
+ | -------------------------------------------------------------------- | ---------------------------------------------------------------------- |
30
+ | **[references/installation.md](references/installation.md)** | Module setup, env vars, config files |
31
+ | **[references/client-auth.md](references/client-auth.md)** | useUserSession, signIn/signUp/signOut, BetterAuthState, safe redirects |
32
+ | **[references/server-auth.md](references/server-auth.md)** | serverAuth, getUserSession, requireUserSession |
33
+ | **[references/route-protection.md](references/route-protection.md)** | routeRules, definePageMeta, middleware |
34
+ | **[references/plugins.md](references/plugins.md)** | Better Auth plugins (admin, passkey, 2FA) |
35
+ | **[references/database.md](references/database.md)** | NuxtHub integration, Drizzle schema |
36
+ | **[references/client-only.md](references/client-only.md)** | External auth backend, clientOnly mode, CORS |
37
+ | **[references/types.md](references/types.md)** | AuthUser, AuthSession, type augmentation |
38
+
39
+ ## Usage Pattern
40
+
41
+ **Load based on context:**
42
+
43
+ - Installing module? → [references/installation.md](references/installation.md)
44
+ - Login/signup forms? → [references/client-auth.md](references/client-auth.md)
45
+ - API route protection? → [references/server-auth.md](references/server-auth.md)
46
+ - Route rules/page meta? → [references/route-protection.md](references/route-protection.md)
47
+ - Using plugins? → [references/plugins.md](references/plugins.md)
48
+ - Database setup? → [references/database.md](references/database.md)
49
+ - External auth backend? → [references/client-only.md](references/client-only.md)
50
+ - TypeScript types? → [references/types.md](references/types.md)
51
+
52
+ **DO NOT read all files at once.** Load based on context.
53
+
54
+ ## Key Concepts
55
+
56
+ | Concept | Description |
57
+ | ---------------------- | --------------------------------------------------------------- |
58
+ | `useUserSession()` | Client composable - user, session, loggedIn, signIn/Out methods |
59
+ | `requireUserSession()` | Server helper - throws 401/403 if not authenticated |
60
+ | `auth` route mode | `'user'`, `'guest'`, `{ user: {...} }`, or `false` |
61
+ | `serverAuth()` | Get Better Auth instance in server routes |
62
+
63
+ ## Quick Reference
64
+
65
+ ```ts
66
+ // Client: useUserSession()
67
+ const { user, loggedIn, signIn, signOut } = useUserSession()
68
+ await signIn.email({ email, password }, { onSuccess: () => navigateTo('/') })
69
+ ```
70
+
71
+ ```ts
72
+ // Server: requireUserSession()
73
+ const { user } = await requireUserSession(event, { user: { role: 'admin' } })
74
+ ```
75
+
76
+ ```ts
77
+ // nuxt.config.ts: Route protection
78
+ routeRules: {
79
+ '/admin/**': { auth: { user: { role: 'admin' } } },
80
+ '/login': { auth: 'guest' },
81
+ '/app/**': { auth: 'user' }
82
+ }
83
+ ```
84
+
85
+ ## Resources
86
+
87
+ - [Module Docs](https://github.com/onmax/nuxt-better-auth)
88
+ - [Better Auth Docs](https://www.better-auth.com/)
89
+
90
+ ---
91
+
92
+ _Token efficiency: Main skill ~300 tokens, each sub-file ~800-1200 tokens_
@@ -0,0 +1,153 @@
1
+ # Client-Side Authentication
2
+
3
+ ## useUserSession()
4
+
5
+ Main composable for auth state and methods.
6
+
7
+ ```ts
8
+ const {
9
+ user, // Ref<AuthUser | null>
10
+ session, // Ref<AuthSession | null>
11
+ loggedIn, // ComputedRef<boolean>
12
+ ready, // ComputedRef<boolean> - session fetch complete
13
+ client, // Better Auth client (client-side only)
14
+ signIn, // Proxy to client.signIn
15
+ signUp, // Proxy to client.signUp
16
+ signOut, // Sign out and clear session
17
+ fetchSession, // Manually refresh session
18
+ updateUser // Optimistic local user update
19
+ } = useUserSession()
20
+ ```
21
+
22
+ ## Sign In
23
+
24
+ ```ts
25
+ // Email/password
26
+ await signIn.email({
27
+ email: 'user@example.com',
28
+ password: 'password123'
29
+ }, {
30
+ onSuccess: () => navigateTo('/dashboard')
31
+ })
32
+
33
+ // OAuth
34
+ await signIn.social({ provider: 'github' })
35
+ ```
36
+
37
+ ## Sign Up
38
+
39
+ ```ts
40
+ await signUp.email({
41
+ email: 'user@example.com',
42
+ password: 'password123',
43
+ name: 'John Doe'
44
+ }, {
45
+ onSuccess: () => navigateTo('/welcome')
46
+ })
47
+ ```
48
+
49
+ ## Sign Out
50
+
51
+ ```ts
52
+ await signOut()
53
+ // or with redirect
54
+ await signOut({ redirect: '/login' })
55
+ ```
56
+
57
+ ## Check Auth State
58
+
59
+ ```vue
60
+ <script setup>
61
+ const { user, loggedIn, ready } = useUserSession()
62
+ </script>
63
+
64
+ <template>
65
+ <div v-if="!ready">Loading...</div>
66
+ <div v-else-if="loggedIn">Welcome, {{ user?.name }}</div>
67
+ <div v-else>Please log in</div>
68
+ </template>
69
+ ```
70
+
71
+ ## Safe Redirects
72
+
73
+ Always validate redirect URLs from query params to prevent open redirects:
74
+
75
+ ```ts
76
+ function getSafeRedirect() {
77
+ const redirect = route.query.redirect as string
78
+ // Must start with / and not // (prevents protocol-relative URLs)
79
+ if (!redirect?.startsWith('/') || redirect.startsWith('//')) {
80
+ return '/'
81
+ }
82
+ return redirect
83
+ }
84
+
85
+ await signIn.email({
86
+ email, password
87
+ }, {
88
+ onSuccess: () => navigateTo(getSafeRedirect())
89
+ })
90
+ ```
91
+
92
+ ## Wait for Session
93
+
94
+ Useful when needing session before rendering:
95
+
96
+ ```ts
97
+ await waitForSession() // 5s timeout
98
+ if (loggedIn.value) {
99
+ // Session is ready
100
+ }
101
+ ```
102
+
103
+ ## Manual Session Refresh
104
+
105
+ ```ts
106
+ // Refetch from server
107
+ await fetchSession({ force: true })
108
+ ```
109
+
110
+ ## Session Management
111
+
112
+ Additional session management via Better Auth client:
113
+
114
+ ```ts
115
+ const { client } = useUserSession()
116
+
117
+ // List all active sessions for current user
118
+ const sessions = await client.listSessions()
119
+
120
+ // Revoke a specific session
121
+ await client.revokeSession({ sessionId: 'xxx' })
122
+
123
+ // Revoke all sessions except current
124
+ await client.revokeOtherSessions()
125
+
126
+ // Revoke all sessions (logs out everywhere)
127
+ await client.revokeSessions()
128
+ ```
129
+
130
+ These methods require the user to be authenticated.
131
+
132
+ ## BetterAuthState Component
133
+
134
+ Renders once session hydration completes (`ready === true`), with loading placeholder support.
135
+
136
+ ```vue
137
+ <BetterAuthState>
138
+ <template #default="{ loggedIn, user, session, signOut }">
139
+ <p v-if="loggedIn">Hi {{ user?.name }}</p>
140
+ <button v-else @click="navigateTo('/login')">Sign in</button>
141
+ </template>
142
+ <template #placeholder>
143
+ <p>Loading…</p>
144
+ </template>
145
+ </BetterAuthState>
146
+ ```
147
+
148
+ **Slots:**
149
+
150
+ - `default` - Renders when `ready === true`, provides `{ loggedIn, user, session, signOut }`
151
+ - `placeholder` - Renders while session hydrates
152
+
153
+ Useful in clientOnly mode or for graceful SSR loading states.
@@ -0,0 +1,87 @@
1
+ # Client-Only Mode (External Auth Backend)
2
+
3
+ When Better Auth runs on a separate backend (microservices, standalone server), use `clientOnly` mode.
4
+
5
+ ## Configuration
6
+
7
+ ### 1. Enable in nuxt.config.ts
8
+
9
+ ```ts
10
+ export default defineNuxtConfig({
11
+ modules: ['@onmax/nuxt-better-auth'],
12
+ auth: {
13
+ clientOnly: true,
14
+ },
15
+ })
16
+ ```
17
+
18
+ ### 2. Point client to external server
19
+
20
+ ```ts [app/auth.config.ts]
21
+ import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
22
+
23
+ export default defineClientAuth({
24
+ baseURL: 'https://auth.example.com', // External auth server
25
+ })
26
+ ```
27
+
28
+ ### 3. Set frontend URL
29
+
30
+ ```ini [.env]
31
+ NUXT_PUBLIC_SITE_URL="https://your-frontend.com"
32
+ ```
33
+
34
+ ## What Changes
35
+
36
+ | Feature | Full Mode | Client-Only |
37
+ | ----------------------------------------------------------------------------- | --------------- | ----------------- |
38
+ | `server/auth.config.ts` | Required | Not needed |
39
+ | `/api/auth/**` handlers | Auto-registered | Skipped |
40
+ | `NUXT_BETTER_AUTH_SECRET` | Required | Not needed |
41
+ | Server utilities (`serverAuth()`, `getUserSession()`, `requireUserSession()`) | Available | **Not available** |
42
+ | SSR session hydration | Server-side | Client-side only |
43
+ | `useUserSession()`, route protection, `<BetterAuthState>` | Works | Works |
44
+
45
+ ## CORS Requirements
46
+
47
+ Ensure external auth server:
48
+
49
+ - Allows requests from frontend (CORS with `credentials: true`)
50
+ - Uses `SameSite=None; Secure` cookies (HTTPS required)
51
+ - Includes frontend URL in `trustedOrigins`
52
+
53
+ ## SSR Considerations
54
+
55
+ Session fetched client-side only:
56
+
57
+ - Server-rendered pages render as "unauthenticated" initially
58
+ - Hydrates with session data on client
59
+ - Use `<BetterAuthState>` for loading states
60
+
61
+ ```vue
62
+ <BetterAuthState v-slot="{ isLoading, user }">
63
+ <div v-if="isLoading">Loading...</div>
64
+ <div v-else-if="user">Welcome, {{ user.name }}</div>
65
+ <div v-else>Please log in</div>
66
+ </BetterAuthState>
67
+ ```
68
+
69
+ ## Use Cases
70
+
71
+ - **Microservices**: Auth service is separate deployment
72
+ - **Shared auth**: Multiple frontends share one auth backend
73
+ - **Existing backend**: Already have Better Auth server running elsewhere
74
+
75
+ ## Architecture Example
76
+
77
+ ```
78
+ ┌─────────────────┐ ┌─────────────────┐
79
+ │ Nuxt App │────▶│ Auth Server │
80
+ │ (clientOnly) │ │ (Better Auth) │
81
+ │ │◀────│ │
82
+ └─────────────────┘ └────────┬────────┘
83
+
84
+ ┌────────▼────────┐
85
+ │ Database │
86
+ └─────────────────┘
87
+ ```
@@ -0,0 +1,115 @@
1
+ # Database Integration
2
+
3
+ ## NuxtHub Setup
4
+
5
+ ```ts
6
+ // nuxt.config.ts
7
+ export default defineNuxtConfig({
8
+ modules: ['@nuxthub/core', '@onmax/nuxt-better-auth'],
9
+ hub: { database: true },
10
+ auth: {
11
+ secondaryStorage: true, // Optional: KV for session caching
12
+ schema: {
13
+ usePlural: false, // user vs users
14
+ casing: 'camelCase' // camelCase or snake_case
15
+ }
16
+ }
17
+ })
18
+ ```
19
+
20
+ ## Schema Generation
21
+
22
+ The module auto-generates Drizzle schema from Better Auth tables. Schema available via:
23
+
24
+ ```ts
25
+ import { user, session, account, verification } from '#auth/database'
26
+ ```
27
+
28
+ ## Database Dialect
29
+
30
+ Supports: `sqlite`, `postgresql`, `mysql`
31
+
32
+ Schema syntax adapts to dialect:
33
+
34
+ - SQLite: `integer('id').primaryKey()`
35
+ - PostgreSQL/MySQL: `uuid('id').primaryKey()` or `text('id').primaryKey()`
36
+
37
+ ## Schema Options
38
+
39
+ ```ts
40
+ auth: {
41
+ schema: {
42
+ usePlural: true, // tables: users, sessions, accounts
43
+ casing: 'snake_case' // columns: created_at, updated_at
44
+ }
45
+ }
46
+ ```
47
+
48
+ | Option | Default | Description |
49
+ | ----------- | ------------- | ------------------------ |
50
+ | `usePlural` | `false` | Pluralize table names |
51
+ | `casing` | `'camelCase'` | Column naming convention |
52
+
53
+ ## Extending Schema
54
+
55
+ Add custom columns via NuxtHub's schema hooks:
56
+
57
+ ```ts
58
+ // server/plugins/extend-schema.ts
59
+ export default defineNitroPlugin(() => {
60
+ useNitroApp().hooks.hook('hub:db:schema:extend', (schema) => {
61
+ // Add custom tables or extend existing
62
+ })
63
+ })
64
+ ```
65
+
66
+ ## Secondary Storage (KV)
67
+
68
+ Enable session caching with KV:
69
+
70
+ ```ts
71
+ auth: {
72
+ secondaryStorage: true
73
+ }
74
+ ```
75
+
76
+ Requires `hub.kv: true` in config. Improves session lookup performance.
77
+
78
+ ## Server Config with DB
79
+
80
+ Database adapter injected via context:
81
+
82
+ ```ts
83
+ // server/auth.config.ts
84
+ import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
85
+
86
+ export default defineServerAuth(({ db }) => ({
87
+ database: db, // Already configured when hub.database: true
88
+ emailAndPassword: { enabled: true }
89
+ }))
90
+ ```
91
+
92
+ ## Manual Database Setup
93
+
94
+ Without NuxtHub, configure manually:
95
+
96
+ ```ts
97
+ // server/auth.config.ts
98
+ import { drizzle } from 'drizzle-orm/...'
99
+ import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
100
+
101
+ const db = drizzle(...)
102
+
103
+ export default defineServerAuth({
104
+ database: drizzleAdapter(db, { provider: 'sqlite' })
105
+ })
106
+ ```
107
+
108
+ ## Migrations
109
+
110
+ Better Auth creates tables automatically on first run. For production, generate migrations:
111
+
112
+ ```bash
113
+ # Using Better Auth CLI
114
+ npx better-auth generate
115
+ ```
@@ -0,0 +1,143 @@
1
+ # Installation & Configuration
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ pnpm add @onmax/nuxt-better-auth better-auth
7
+ ```
8
+
9
+ **Version Requirements:**
10
+
11
+ - `@onmax/nuxt-better-auth`: `^0.0.2-alpha.12` (alpha)
12
+ - `better-auth`: `^1.0.0` (module tested with `1.4.7`)
13
+ - `@nuxthub/core`: `^0.10.0` (optional, for database)
14
+
15
+ ## Module Setup
16
+
17
+ ```ts
18
+ // nuxt.config.ts
19
+ export default defineNuxtConfig({
20
+ modules: ['@onmax/nuxt-better-auth'],
21
+ auth: {
22
+ serverConfig: 'server/auth.config', // default
23
+ clientConfig: 'app/auth.config', // default
24
+ clientOnly: false, // true for external auth backend
25
+ redirects: {
26
+ login: '/login', // redirect when auth required
27
+ guest: '/' // redirect when already logged in
28
+ }
29
+ }
30
+ })
31
+ ```
32
+
33
+ ## Environment Variables
34
+
35
+ ```bash
36
+ # Required (min 32 chars)
37
+ BETTER_AUTH_SECRET=your-secret-key-at-least-32-characters
38
+
39
+ # Required in production for OAuth
40
+ NUXT_PUBLIC_SITE_URL=https://your-domain.com
41
+ ```
42
+
43
+ ## Server Config
44
+
45
+ ```ts
46
+ // server/auth.config.ts
47
+ import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
48
+
49
+ // Object syntax (simplest)
50
+ export default defineServerAuth({
51
+ emailAndPassword: { enabled: true },
52
+ // OAuth providers
53
+ socialProviders: {
54
+ github: {
55
+ clientId: process.env.GITHUB_CLIENT_ID as string,
56
+ clientSecret: process.env.GITHUB_CLIENT_SECRET as string
57
+ }
58
+ },
59
+ })
60
+
61
+ // Or function syntax (access context like runtimeConfig, db)
62
+ export default defineServerAuth(({ runtimeConfig, db }) => ({
63
+ emailAndPassword: { enabled: true },
64
+ socialProviders: {
65
+ github: {
66
+ clientId: runtimeConfig.github.clientId,
67
+ clientSecret: runtimeConfig.github.clientSecret
68
+ }
69
+ },
70
+ session: {
71
+ expiresIn: 60 * 60 * 24 * 7, // 7 days (default)
72
+ updateAge: 60 * 60 * 24, // Update every 24h (default)
73
+ freshAge: 60 * 60 * 24, // Consider fresh for 24h (default, 0 to disable)
74
+ cookieCache: {
75
+ enabled: true,
76
+ maxAge: 60 * 5 // 5 minutes cookie cache
77
+ }
78
+ }
79
+ }))
80
+ ```
81
+
82
+ Context available in `defineServerAuth`:
83
+
84
+ - `runtimeConfig` - Nuxt runtime config
85
+ - `db` - Database adapter (when NuxtHub enabled)
86
+
87
+ ### Session Options
88
+
89
+ | Option | Default | Description |
90
+ | ----------------------- | ------------------ | --------------------------------------------- |
91
+ | `expiresIn` | `604800` (7 days) | Session lifetime in seconds |
92
+ | `updateAge` | `86400` (24 hours) | How often to refresh session expiry |
93
+ | `freshAge` | `86400` (24 hours) | Session considered "fresh" period (0 = never) |
94
+ | `cookieCache.enabled` | `false` | Enable cookie caching to reduce DB queries |
95
+ | `cookieCache.maxAge` | `300` (5 minutes) | Cookie cache lifetime |
96
+ | `disableSessionRefresh` | `false` | Disable automatic session refresh |
97
+
98
+ ## Client Config
99
+
100
+ ```ts
101
+ // app/auth.config.ts
102
+ import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
103
+
104
+ // Object syntax (simplest)
105
+ export default defineClientAuth({
106
+ // Client-side plugin options (e.g., passkey, twoFactor)
107
+ })
108
+
109
+ // Or function syntax (access context like siteUrl)
110
+ export default defineClientAuth(({ siteUrl }) => ({
111
+ // siteUrl contains the resolved base URL
112
+ }))
113
+ ```
114
+
115
+ ## NuxtHub Integration
116
+
117
+ ```ts
118
+ // nuxt.config.ts
119
+ export default defineNuxtConfig({
120
+ modules: ['@nuxthub/core', '@onmax/nuxt-better-auth'],
121
+ hub: { database: true },
122
+ auth: {
123
+ secondaryStorage: true // Enable KV for session caching
124
+ }
125
+ })
126
+ ```
127
+
128
+ See [references/database.md](database.md) for schema setup.
129
+
130
+ ## Client-Only Mode
131
+
132
+ For external auth backends (microservices, separate servers):
133
+
134
+ ```ts
135
+ // nuxt.config.ts
136
+ export default defineNuxtConfig({
137
+ auth: {
138
+ clientOnly: true, // No local auth server
139
+ }
140
+ })
141
+ ```
142
+
143
+ See [references/client-only.md](client-only.md) for full setup.