create-nuxt-base 1.0.3 → 1.1.1
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/AUTH.md +289 -0
- package/CHANGELOG.md +24 -0
- package/README.md +41 -33
- package/nuxt-base-template/app/composables/use-better-auth.ts +393 -8
- package/nuxt-base-template/app/lib/auth-client.ts +175 -78
- package/nuxt-base-template/app/pages/app/index.vue +101 -0
- package/nuxt-base-template/app/pages/app/settings/security.vue +30 -13
- package/nuxt-base-template/app/pages/auth/2fa.vue +23 -8
- package/nuxt-base-template/app/pages/auth/login.vue +71 -7
- package/nuxt-base-template/app/pages/auth/register.vue +49 -77
- package/nuxt-base-template/app/utils/crypto.ts +31 -0
- package/nuxt-base-template/server/api/iam/[...path].ts +65 -0
- package/package.json +1 -1
package/AUTH.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Better Auth Integration
|
|
2
|
+
|
|
3
|
+
This document describes the Better Auth integration in the nuxt-base-starter template.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The template uses [Better Auth](https://www.better-auth.com/) for authentication with the following features:
|
|
8
|
+
|
|
9
|
+
| Feature | Status | Description |
|
|
10
|
+
|-----------------------|--------|----------------------------------------|
|
|
11
|
+
| Email & Password | ✅ | Standard email/password authentication |
|
|
12
|
+
| Two-Factor Auth (2FA) | ✅ | TOTP-based 2FA with backup codes |
|
|
13
|
+
| Passkey (WebAuthn) | ✅ | Passwordless authentication |
|
|
14
|
+
| Session Management | ✅ | Cookie-based sessions with SSR support |
|
|
15
|
+
| Password Hashing | ✅ | Client-side SHA256 hashing |
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
21
|
+
│ FRONTEND (Nuxt) │
|
|
22
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ │
|
|
24
|
+
│ ┌─────────────────┐ ┌─────────────────┐ │
|
|
25
|
+
│ │ auth-client │───▶│ useBetterAuth │ │
|
|
26
|
+
│ │ (lib/) │ │ (composable) │ │
|
|
27
|
+
│ └────────┬────────┘ └────────┬────────┘ │
|
|
28
|
+
│ │ │ │
|
|
29
|
+
│ │ SHA256 Hashing │ Cookie-based State │
|
|
30
|
+
│ │ Plugin Config │ Session Validation │
|
|
31
|
+
│ │ │ │
|
|
32
|
+
└───────────┼──────────────────────┼──────────────────────────────┘
|
|
33
|
+
│ │
|
|
34
|
+
▼ ▼
|
|
35
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ BACKEND (nest-server) │
|
|
37
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
38
|
+
│ /iam/sign-in/email /iam/session │
|
|
39
|
+
│ /iam/sign-up/email /iam/sign-out │
|
|
40
|
+
│ /iam/passkey/* /iam/two-factor/* │
|
|
41
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Files
|
|
45
|
+
|
|
46
|
+
| File | Purpose |
|
|
47
|
+
|--------------------------------------|----------------------------------|
|
|
48
|
+
| `app/lib/auth-client.ts` | Better Auth client configuration |
|
|
49
|
+
| `app/composables/use-better-auth.ts` | Auth state management composable |
|
|
50
|
+
| `app/pages/auth/login.vue` | Login page |
|
|
51
|
+
| `app/pages/auth/register.vue` | Registration page |
|
|
52
|
+
| `app/pages/auth/2fa.vue` | Two-factor authentication page |
|
|
53
|
+
| `app/pages/auth/forgot-password.vue` | Password reset request |
|
|
54
|
+
| `app/pages/auth/reset-password.vue` | Password reset form |
|
|
55
|
+
| `app/utils/crypto.ts` | SHA256 hashing utility |
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Basic Authentication
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// In a Vue component
|
|
63
|
+
const { signIn, signUp, signOut, user, isAuthenticated } = useBetterAuth();
|
|
64
|
+
|
|
65
|
+
// Sign in
|
|
66
|
+
const result = await signIn.email({
|
|
67
|
+
email: 'user@example.com',
|
|
68
|
+
password: 'password123',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Sign up
|
|
72
|
+
const result = await signUp.email({
|
|
73
|
+
email: 'user@example.com',
|
|
74
|
+
name: 'John Doe',
|
|
75
|
+
password: 'password123',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Sign out
|
|
79
|
+
await signOut();
|
|
80
|
+
|
|
81
|
+
// Check auth state
|
|
82
|
+
if (isAuthenticated.value) {
|
|
83
|
+
console.log('User:', user.value);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Passkey Authentication
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { authClient } from '~/lib/auth-client';
|
|
91
|
+
|
|
92
|
+
// Sign in with passkey
|
|
93
|
+
const result = await authClient.signIn.passkey();
|
|
94
|
+
|
|
95
|
+
if (result.error) {
|
|
96
|
+
console.error('Passkey login failed:', result.error.message);
|
|
97
|
+
} else {
|
|
98
|
+
// Validate session to get user data (passkey returns session only)
|
|
99
|
+
await validateSession();
|
|
100
|
+
navigateTo('/app');
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Two-Factor Authentication
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { authClient } from '~/lib/auth-client';
|
|
108
|
+
|
|
109
|
+
// Verify TOTP code
|
|
110
|
+
const result = await authClient.twoFactor.verifyTotp({
|
|
111
|
+
code: '123456',
|
|
112
|
+
trustDevice: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Verify backup code
|
|
116
|
+
const result = await authClient.twoFactor.verifyBackupCode({
|
|
117
|
+
code: 'backup-code-here',
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Session Validation
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const { validateSession, user } = useBetterAuth();
|
|
125
|
+
|
|
126
|
+
// On app init, validate the session
|
|
127
|
+
const isValid = await validateSession();
|
|
128
|
+
|
|
129
|
+
if (isValid) {
|
|
130
|
+
console.log('Session valid, user:', user.value);
|
|
131
|
+
} else {
|
|
132
|
+
console.log('No valid session');
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Configuration
|
|
137
|
+
|
|
138
|
+
### Environment Variables
|
|
139
|
+
|
|
140
|
+
```env
|
|
141
|
+
# API URL (required)
|
|
142
|
+
API_URL=http://localhost:3000
|
|
143
|
+
|
|
144
|
+
# Or via Vite
|
|
145
|
+
VITE_API_URL=http://localhost:3000
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Custom Configuration
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { createBetterAuthClient } from '~/lib/auth-client';
|
|
152
|
+
|
|
153
|
+
// Create a custom client
|
|
154
|
+
const customClient = createBetterAuthClient({
|
|
155
|
+
baseURL: 'https://api.example.com',
|
|
156
|
+
basePath: '/auth', // Default: '/iam'
|
|
157
|
+
twoFactorRedirectPath: '/login/2fa', // Default: '/auth/2fa'
|
|
158
|
+
enableAdmin: false,
|
|
159
|
+
enableTwoFactor: true,
|
|
160
|
+
enablePasskey: true,
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Security
|
|
165
|
+
|
|
166
|
+
### Password Hashing
|
|
167
|
+
|
|
168
|
+
Passwords are hashed with SHA256 on the client-side before transmission:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// This happens automatically in auth-client.ts
|
|
172
|
+
const hashedPassword = await sha256(plainPassword);
|
|
173
|
+
// Result: 64-character hex string
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Why client-side hashing?**
|
|
177
|
+
1. Prevents plain text passwords in network logs
|
|
178
|
+
2. Works with nest-server's `normalizePasswordForIam()` which detects SHA256 hashes
|
|
179
|
+
3. Server re-hashes with bcrypt for storage
|
|
180
|
+
|
|
181
|
+
### Cookie-Based Sessions
|
|
182
|
+
|
|
183
|
+
Sessions are stored in cookies for SSR compatibility:
|
|
184
|
+
|
|
185
|
+
| Cookie | Purpose |
|
|
186
|
+
|-----------------------------|----------------------------|
|
|
187
|
+
| `auth-state` | User data (SSR-compatible) |
|
|
188
|
+
| `token` | Session token |
|
|
189
|
+
| `better-auth.session_token` | Better Auth native cookie |
|
|
190
|
+
|
|
191
|
+
### Cross-Origin Requests
|
|
192
|
+
|
|
193
|
+
The client is configured with `credentials: 'include'` for cross-origin cookie handling:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// In auth-client.ts
|
|
197
|
+
fetchOptions: {
|
|
198
|
+
credentials: 'include',
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Backend CORS Configuration:**
|
|
203
|
+
```typescript
|
|
204
|
+
// In nest-server config
|
|
205
|
+
cors: {
|
|
206
|
+
origin: 'http://localhost:3001', // Not '*'
|
|
207
|
+
credentials: true,
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Better Auth Endpoints
|
|
212
|
+
|
|
213
|
+
The following endpoints are provided by the nest-server backend:
|
|
214
|
+
|
|
215
|
+
### Authentication
|
|
216
|
+
|
|
217
|
+
| Endpoint | Method | Description |
|
|
218
|
+
|----------------------|--------|-----------------------------|
|
|
219
|
+
| `/iam/sign-in/email` | POST | Email/password sign in |
|
|
220
|
+
| `/iam/sign-up/email` | POST | Email/password registration |
|
|
221
|
+
| `/iam/sign-out` | POST | Sign out |
|
|
222
|
+
| `/iam/session` | GET | Get current session |
|
|
223
|
+
|
|
224
|
+
### Passkey (WebAuthn)
|
|
225
|
+
|
|
226
|
+
| Endpoint | Method | Description |
|
|
227
|
+
|----------------------------------------------|--------|--------------------------|
|
|
228
|
+
| `/iam/passkey/generate-register-options` | GET | Get registration options |
|
|
229
|
+
| `/iam/passkey/verify-registration` | POST | Verify registration |
|
|
230
|
+
| `/iam/passkey/generate-authenticate-options` | GET | Get auth options |
|
|
231
|
+
| `/iam/passkey/verify-authentication` | POST | Verify authentication |
|
|
232
|
+
| `/iam/passkey/list-user-passkeys` | GET | List user's passkeys |
|
|
233
|
+
| `/iam/passkey/delete-passkey` | POST | Delete a passkey |
|
|
234
|
+
|
|
235
|
+
### Two-Factor Authentication
|
|
236
|
+
|
|
237
|
+
| Endpoint | Method | Description |
|
|
238
|
+
|--------------------------------------|--------|--------------------|
|
|
239
|
+
| `/iam/two-factor/enable` | POST | Enable 2FA |
|
|
240
|
+
| `/iam/two-factor/disable` | POST | Disable 2FA |
|
|
241
|
+
| `/iam/two-factor/verify-totp` | POST | Verify TOTP code |
|
|
242
|
+
| `/iam/two-factor/verify-backup-code` | POST | Verify backup code |
|
|
243
|
+
|
|
244
|
+
## Troubleshooting
|
|
245
|
+
|
|
246
|
+
### "Passkey not found" Error
|
|
247
|
+
|
|
248
|
+
1. Ensure the user has registered a passkey first
|
|
249
|
+
2. Check that cookies are being sent (`credentials: 'include'`)
|
|
250
|
+
3. Verify CORS is configured correctly on the backend
|
|
251
|
+
|
|
252
|
+
### 2FA Redirect Not Working
|
|
253
|
+
|
|
254
|
+
Ensure the 2FA redirect is handled in the login page:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// Check for 2FA redirect in login response
|
|
258
|
+
if (result.data?.twoFactorRedirect) {
|
|
259
|
+
await navigateTo('/auth/2fa');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Session Not Persisting After Passkey Login
|
|
265
|
+
|
|
266
|
+
The passkey response only contains the session, not the user. Call `validateSession()`:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
if (result.data?.session) {
|
|
270
|
+
await validateSession(); // Fetches user data
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Form Not Submitting (Nuxt UI)
|
|
275
|
+
|
|
276
|
+
Ensure UForm has the `:state` binding:
|
|
277
|
+
|
|
278
|
+
```vue
|
|
279
|
+
<UForm :schema="schema" :state="formState" @submit="onSubmit">
|
|
280
|
+
<UInput v-model="formState.field" />
|
|
281
|
+
</UForm>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## References
|
|
285
|
+
|
|
286
|
+
- [Better Auth Documentation](https://www.better-auth.com/docs)
|
|
287
|
+
- [Better Auth Passkey Plugin](https://www.better-auth.com/docs/plugins/passkey)
|
|
288
|
+
- [Better Auth Two-Factor Plugin](https://www.better-auth.com/docs/plugins/two-factor)
|
|
289
|
+
- [nest-server Better Auth Integration](https://github.com/lenneTech/nest-server)
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.1.1](https://github.com/lenneTech/nuxt-base-starter/compare/v1.1.0...v1.1.1) (2026-01-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **auth:** auto-login after registration and improved passkey handling ([625128b](https://github.com/lenneTech/nuxt-base-starter/commit/625128b18fe812c141859946c196f8efb0738dca))
|
|
11
|
+
* **auth:** improve 2FA UX and document dev-mode proxy requirements ([4c4b1f4](https://github.com/lenneTech/nuxt-base-starter/commit/4c4b1f4d8b77fa93469ccc1a31d4f3292cc7c724))
|
|
12
|
+
|
|
13
|
+
## [1.1.0](https://github.com/lenneTech/nuxt-base-starter/compare/v1.0.3...v1.1.0) (2026-01-20)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* add complete Better-Auth integration with Passkey support and comprehensive documentation ([e0d470c](https://github.com/lenneTech/nuxt-base-starter/commit/e0d470c8229c37bed2948d929676620f344f4878))
|
|
19
|
+
|
|
20
|
+
## [1.2.0](https://github.com/lenneTech/nuxt-base-starter/compare/v1.0.3...v1.2.0) (2026-01-20)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* add complete Better-Auth integration with Passkey support and comprehensive documentation ([70fbec1](https://github.com/lenneTech/nuxt-base-starter/commit/70fbec14e38673c5185195fe05f0cd82bf72a800))
|
|
26
|
+
|
|
27
|
+
## [1.1.0](https://github.com/lenneTech/nuxt-base-starter/compare/v1.0.3...v1.1.0) (2026-01-20)
|
|
28
|
+
|
|
5
29
|
### [1.0.3](https://github.com/lenneTech/nuxt-base-starter/compare/v1.0.2...v1.0.3) (2026-01-12)
|
|
6
30
|
|
|
7
31
|
### [1.0.2](https://github.com/lenneTech/nuxt-base-starter/compare/v1.0.1...v1.0.2) (2026-01-12)
|
package/README.md
CHANGED
|
@@ -16,29 +16,37 @@ The development server starts at **http://localhost:3001**
|
|
|
16
16
|
|
|
17
17
|
### Core Framework
|
|
18
18
|
|
|
19
|
-
| Technology
|
|
20
|
-
|
|
21
|
-
| Nuxt
|
|
22
|
-
| TypeScript
|
|
23
|
-
| Tailwind CSS | 4.x
|
|
24
|
-
| NuxtUI
|
|
19
|
+
| Technology | Version | Description |
|
|
20
|
+
|--------------|---------|---------------------------------------|
|
|
21
|
+
| Nuxt | 4.x | Vue 3 meta-framework with SSR support |
|
|
22
|
+
| TypeScript | 5.9.x | Strict type checking enabled |
|
|
23
|
+
| Tailwind CSS | 4.x | Utility-first CSS with Vite plugin |
|
|
24
|
+
| NuxtUI | 4.x | Component library with dark mode |
|
|
25
25
|
|
|
26
26
|
### Authentication (Better Auth)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
Complete authentication system using [Better Auth](https://www.better-auth.com/):
|
|
29
|
+
|
|
30
|
+
| Feature | Description |
|
|
31
|
+
|--------------------|-------------------------------------------------------|
|
|
32
|
+
| Email/Password | Standard auth with client-side SHA256 hashing |
|
|
33
|
+
| Two-Factor (2FA) | TOTP-based 2FA with backup codes |
|
|
34
|
+
| Passkey/WebAuthn | Passwordless authentication (Touch ID, Face ID, etc.) |
|
|
35
|
+
| Password Reset | Email-based password reset flow |
|
|
36
|
+
| Session Management | SSR-compatible cookie-based sessions |
|
|
37
|
+
|
|
38
|
+
Pre-built auth pages: login, register, forgot-password, reset-password, 2fa
|
|
39
|
+
|
|
40
|
+
📖 **See [AUTH.md](./AUTH.md) for detailed documentation**
|
|
33
41
|
|
|
34
42
|
### State & Data
|
|
35
43
|
|
|
36
|
-
| Package
|
|
37
|
-
|
|
38
|
-
| Pinia
|
|
39
|
-
| VueUse
|
|
40
|
-
| @hey-api/client-fetch | Type-safe API client
|
|
41
|
-
| Valibot
|
|
44
|
+
| Package | Purpose |
|
|
45
|
+
|-----------------------|-----------------------------|
|
|
46
|
+
| Pinia | State management |
|
|
47
|
+
| VueUse | Vue composition utilities |
|
|
48
|
+
| @hey-api/client-fetch | Type-safe API client |
|
|
49
|
+
| Valibot | Schema validation for forms |
|
|
42
50
|
|
|
43
51
|
### SEO & Analytics
|
|
44
52
|
|
|
@@ -48,13 +56,13 @@ The development server starts at **http://localhost:3001**
|
|
|
48
56
|
|
|
49
57
|
### Developer Experience
|
|
50
58
|
|
|
51
|
-
| Tool
|
|
52
|
-
|
|
53
|
-
| OxLint
|
|
54
|
-
| OxFmt
|
|
55
|
-
| Playwright
|
|
59
|
+
| Tool | Purpose |
|
|
60
|
+
|--------------------|------------------------------------|
|
|
61
|
+
| OxLint | Fast linting |
|
|
62
|
+
| OxFmt | Code formatting |
|
|
63
|
+
| Playwright | E2E testing |
|
|
56
64
|
| @lenne.tech/bug.lt | Bug reporting to Linear (dev only) |
|
|
57
|
-
| dayjs-nuxt
|
|
65
|
+
| dayjs-nuxt | Date/time handling |
|
|
58
66
|
|
|
59
67
|
### File Upload
|
|
60
68
|
|
|
@@ -95,17 +103,17 @@ my-project/
|
|
|
95
103
|
|
|
96
104
|
## Available Scripts
|
|
97
105
|
|
|
98
|
-
| Script
|
|
99
|
-
|
|
100
|
-
| `npm run dev`
|
|
101
|
-
| `npm run build`
|
|
102
|
-
| `npm run preview`
|
|
106
|
+
| Script | Description |
|
|
107
|
+
|--------------------------|----------------------------------------|
|
|
108
|
+
| `npm run dev` | Start development server |
|
|
109
|
+
| `npm run build` | Build for production |
|
|
110
|
+
| `npm run preview` | Preview production build |
|
|
103
111
|
| `npm run generate-types` | Generate TypeScript types from OpenAPI |
|
|
104
|
-
| `npm run test`
|
|
105
|
-
| `npm run lint`
|
|
106
|
-
| `npm run format`
|
|
107
|
-
| `npm run check`
|
|
108
|
-
| `npm run fix`
|
|
112
|
+
| `npm run test` | Run Playwright E2E tests |
|
|
113
|
+
| `npm run lint` | Run OxLint |
|
|
114
|
+
| `npm run format` | Run OxFmt |
|
|
115
|
+
| `npm run check` | Run lint + format check |
|
|
116
|
+
| `npm run fix` | Auto-fix lint + format issues |
|
|
109
117
|
|
|
110
118
|
## Environment Variables
|
|
111
119
|
|