@opensaas/stack-auth 0.1.0
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/.turbo/turbo-build.log +4 -0
- package/INTEGRATION_SUMMARY.md +425 -0
- package/README.md +445 -0
- package/dist/client/index.d.ts +38 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +23 -0
- package/dist/client/index.js.map +1 -0
- package/dist/config/index.d.ts +50 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +115 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +160 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/lists/index.d.ts +46 -0
- package/dist/lists/index.d.ts.map +1 -0
- package/dist/lists/index.js +227 -0
- package/dist/lists/index.js.map +1 -0
- package/dist/server/index.d.ts +27 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +90 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/components/ForgotPasswordForm.d.ts +36 -0
- package/dist/ui/components/ForgotPasswordForm.d.ts.map +1 -0
- package/dist/ui/components/ForgotPasswordForm.js +50 -0
- package/dist/ui/components/ForgotPasswordForm.js.map +1 -0
- package/dist/ui/components/SignInForm.d.ts +52 -0
- package/dist/ui/components/SignInForm.d.ts.map +1 -0
- package/dist/ui/components/SignInForm.js +66 -0
- package/dist/ui/components/SignInForm.js.map +1 -0
- package/dist/ui/components/SignUpForm.d.ts +56 -0
- package/dist/ui/components/SignUpForm.d.ts.map +1 -0
- package/dist/ui/components/SignUpForm.js +74 -0
- package/dist/ui/components/SignUpForm.js.map +1 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +4 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +55 -0
- package/src/client/index.ts +44 -0
- package/src/config/index.ts +140 -0
- package/src/config/types.ts +166 -0
- package/src/index.ts +44 -0
- package/src/lists/index.ts +245 -0
- package/src/server/index.ts +120 -0
- package/src/ui/components/ForgotPasswordForm.tsx +120 -0
- package/src/ui/components/SignInForm.tsx +191 -0
- package/src/ui/components/SignUpForm.tsx +238 -0
- package/src/ui/index.ts +7 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# @opensaas/stack-auth
|
|
2
|
+
|
|
3
|
+
Better-auth integration for OpenSaas Stack - Add authentication to your app in minutes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Auto-generated Auth Tables** - User, Session, Account, and Verification lists created automatically
|
|
8
|
+
- **Session Integration** - Seamless integration with OpenSaas access control system
|
|
9
|
+
- **Pre-built UI Components** - Sign in, sign up, and forgot password forms ready to use
|
|
10
|
+
- **Multiple Auth Methods** - Email/password, OAuth providers (GitHub, Google, etc.)
|
|
11
|
+
- **Email Verification** - Built-in email verification support
|
|
12
|
+
- **Password Reset** - Forgot password flow included
|
|
13
|
+
- **Type-Safe** - Full TypeScript support with automatic type generation
|
|
14
|
+
- **Configurable Sessions** - Choose which user fields to include in session
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @opensaas/stack-auth better-auth
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Update Your Config
|
|
25
|
+
|
|
26
|
+
Wrap your OpenSaas config with `withAuth()`:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// opensaas.config.ts
|
|
30
|
+
import { config } from '@opensaas/stack-core'
|
|
31
|
+
import { withAuth, authConfig } from '@opensaas/stack-auth'
|
|
32
|
+
|
|
33
|
+
export default withAuth(
|
|
34
|
+
config({
|
|
35
|
+
db: {
|
|
36
|
+
provider: 'sqlite',
|
|
37
|
+
url: process.env.DATABASE_URL || 'file:./dev.db',
|
|
38
|
+
},
|
|
39
|
+
lists: {
|
|
40
|
+
// Your custom lists here
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
authConfig({
|
|
44
|
+
emailAndPassword: { enabled: true },
|
|
45
|
+
emailVerification: { enabled: true },
|
|
46
|
+
passwordReset: { enabled: true },
|
|
47
|
+
sessionFields: ['userId', 'email', 'name'],
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Generate Schema and Push to Database
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm generate # Generates Prisma schema with auth tables
|
|
56
|
+
pnpm db:push # Push schema to database
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Create Auth Server Instance
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// lib/auth.ts
|
|
63
|
+
import { createAuth } from '@opensaas/stack-auth/server'
|
|
64
|
+
import config from '../opensaas.config'
|
|
65
|
+
|
|
66
|
+
export const auth = createAuth(config)
|
|
67
|
+
|
|
68
|
+
// Export auth API for route handlers
|
|
69
|
+
export const GET = auth.handler
|
|
70
|
+
export const POST = auth.handler
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 4. Set Up Auth Route
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// app/api/auth/[...all]/route.ts
|
|
77
|
+
export { GET, POST } from '@/lib/auth'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 5. Create Auth Client
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// lib/auth-client.ts
|
|
84
|
+
'use client'
|
|
85
|
+
|
|
86
|
+
import { createClient } from '@opensaas/stack-auth/client'
|
|
87
|
+
|
|
88
|
+
export const authClient = createClient({
|
|
89
|
+
baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 6. Add Sign In Page
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// app/sign-in/page.tsx
|
|
97
|
+
import { SignInForm } from '@opensaas/stack-auth/ui'
|
|
98
|
+
import { authClient } from '@/lib/auth-client'
|
|
99
|
+
|
|
100
|
+
export default function SignInPage() {
|
|
101
|
+
return (
|
|
102
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
103
|
+
<SignInForm authClient={authClient} redirectTo="/admin" />
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 7. Use Session in Access Control
|
|
110
|
+
|
|
111
|
+
Sessions are now automatically available in your access control functions:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// opensaas.config.ts
|
|
115
|
+
import { withAuth, authConfig } from '@opensaas/stack-auth'
|
|
116
|
+
|
|
117
|
+
export default withAuth(
|
|
118
|
+
config({
|
|
119
|
+
lists: {
|
|
120
|
+
Post: list({
|
|
121
|
+
fields: { title: text(), content: text() },
|
|
122
|
+
access: {
|
|
123
|
+
operation: {
|
|
124
|
+
// Session is automatically populated from better-auth
|
|
125
|
+
create: ({ session }) => !!session,
|
|
126
|
+
update: ({ session, item }) => {
|
|
127
|
+
if (!session) return false
|
|
128
|
+
return { authorId: { equals: session.userId } }
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
authConfig({
|
|
136
|
+
emailAndPassword: { enabled: true },
|
|
137
|
+
// Session will contain: { userId, email, name }
|
|
138
|
+
sessionFields: ['userId', 'email', 'name'],
|
|
139
|
+
}),
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
### Auth Config Options
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
authConfig({
|
|
149
|
+
// Email/password authentication
|
|
150
|
+
emailAndPassword: {
|
|
151
|
+
enabled: true,
|
|
152
|
+
minPasswordLength: 8,
|
|
153
|
+
requireConfirmation: true,
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// Email verification
|
|
157
|
+
emailVerification: {
|
|
158
|
+
enabled: true,
|
|
159
|
+
sendOnSignUp: true,
|
|
160
|
+
tokenExpiration: 86400, // 24 hours in seconds
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Password reset
|
|
164
|
+
passwordReset: {
|
|
165
|
+
enabled: true,
|
|
166
|
+
tokenExpiration: 3600, // 1 hour in seconds
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
// OAuth providers
|
|
170
|
+
socialProviders: {
|
|
171
|
+
github: {
|
|
172
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
173
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
174
|
+
},
|
|
175
|
+
google: {
|
|
176
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
177
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
// Session configuration
|
|
182
|
+
session: {
|
|
183
|
+
expiresIn: 604800, // 7 days in seconds
|
|
184
|
+
updateAge: true, // Refresh session on each request
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// Fields to include in session object
|
|
188
|
+
sessionFields: ['userId', 'email', 'name', 'role'],
|
|
189
|
+
|
|
190
|
+
// Extend User list with custom fields
|
|
191
|
+
extendUserList: {
|
|
192
|
+
fields: {
|
|
193
|
+
role: text({ defaultValue: 'user' }),
|
|
194
|
+
company: text(),
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
// Custom email sending function
|
|
199
|
+
sendEmail: async ({ to, subject, html }) => {
|
|
200
|
+
await yourEmailService.send({ to, subject, html })
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## UI Components
|
|
206
|
+
|
|
207
|
+
### SignInForm
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { SignInForm } from '@opensaas/stack-auth/ui'
|
|
211
|
+
import { authClient } from '@/lib/auth-client'
|
|
212
|
+
|
|
213
|
+
<SignInForm
|
|
214
|
+
authClient={authClient}
|
|
215
|
+
redirectTo="/dashboard"
|
|
216
|
+
showSocialProviders={true}
|
|
217
|
+
socialProviders={['github', 'google']}
|
|
218
|
+
onSuccess={() => console.log('Signed in!')}
|
|
219
|
+
onError={(error) => console.error(error)}
|
|
220
|
+
/>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### SignUpForm
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { SignUpForm } from '@opensaas/stack-auth/ui'
|
|
227
|
+
import { authClient } from '@/lib/auth-client'
|
|
228
|
+
|
|
229
|
+
<SignUpForm
|
|
230
|
+
authClient={authClient}
|
|
231
|
+
redirectTo="/dashboard"
|
|
232
|
+
requirePasswordConfirmation={true}
|
|
233
|
+
onSuccess={() => console.log('Account created!')}
|
|
234
|
+
/>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### ForgotPasswordForm
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { ForgotPasswordForm } from '@opensaas/stack-auth/ui'
|
|
241
|
+
import { authClient } from '@/lib/auth-client'
|
|
242
|
+
|
|
243
|
+
<ForgotPasswordForm
|
|
244
|
+
authClient={authClient}
|
|
245
|
+
onSuccess={() => console.log('Reset email sent!')}
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Auto-Generated Lists
|
|
250
|
+
|
|
251
|
+
The following lists are automatically created when you use `withAuth()`:
|
|
252
|
+
|
|
253
|
+
### User
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
{
|
|
257
|
+
id: string
|
|
258
|
+
name: string
|
|
259
|
+
email: string (unique)
|
|
260
|
+
emailVerified: boolean
|
|
261
|
+
image?: string
|
|
262
|
+
sessions: Session[] (relationship)
|
|
263
|
+
accounts: Account[] (relationship)
|
|
264
|
+
createdAt: Date
|
|
265
|
+
updatedAt: Date
|
|
266
|
+
// Plus any custom fields you add via extendUserList
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Session
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
{
|
|
274
|
+
id: string
|
|
275
|
+
token: string (unique)
|
|
276
|
+
userId: string
|
|
277
|
+
expiresAt: Date
|
|
278
|
+
ipAddress?: string
|
|
279
|
+
userAgent?: string
|
|
280
|
+
user: User (relationship)
|
|
281
|
+
createdAt: Date
|
|
282
|
+
updatedAt: Date
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Account
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
{
|
|
290
|
+
id: string
|
|
291
|
+
userId: string
|
|
292
|
+
accountId: string
|
|
293
|
+
providerId: string ('github', 'google', 'credentials', etc.)
|
|
294
|
+
accessToken?: string
|
|
295
|
+
refreshToken?: string
|
|
296
|
+
accessTokenExpiresAt?: Date
|
|
297
|
+
refreshTokenExpiresAt?: Date
|
|
298
|
+
scope?: string
|
|
299
|
+
idToken?: string
|
|
300
|
+
password?: string
|
|
301
|
+
user: User (relationship)
|
|
302
|
+
createdAt: Date
|
|
303
|
+
updatedAt: Date
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Verification
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
{
|
|
311
|
+
id: string
|
|
312
|
+
identifier: string (e.g., email address)
|
|
313
|
+
value: string (token)
|
|
314
|
+
expiresAt: Date
|
|
315
|
+
createdAt: Date
|
|
316
|
+
updatedAt: Date
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Extending the User List
|
|
321
|
+
|
|
322
|
+
Add custom fields to the User model:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
authConfig({
|
|
326
|
+
extendUserList: {
|
|
327
|
+
fields: {
|
|
328
|
+
role: select({
|
|
329
|
+
options: [
|
|
330
|
+
{ label: 'User', value: 'user' },
|
|
331
|
+
{ label: 'Admin', value: 'admin' },
|
|
332
|
+
],
|
|
333
|
+
defaultValue: 'user',
|
|
334
|
+
}),
|
|
335
|
+
company: text(),
|
|
336
|
+
phoneNumber: text(),
|
|
337
|
+
},
|
|
338
|
+
// Optionally override access control
|
|
339
|
+
access: {
|
|
340
|
+
operation: {
|
|
341
|
+
query: () => true,
|
|
342
|
+
create: () => true,
|
|
343
|
+
update: ({ session, item }) => session?.userId === item?.id,
|
|
344
|
+
delete: ({ session }) => session?.role === 'admin',
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Session Fields
|
|
352
|
+
|
|
353
|
+
Control which user fields are included in the session object:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
authConfig({
|
|
357
|
+
sessionFields: ['userId', 'email', 'name', 'role'],
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
// Now in access control:
|
|
361
|
+
access: {
|
|
362
|
+
operation: {
|
|
363
|
+
create: ({ session }) => {
|
|
364
|
+
console.log(session) // { userId, email, name, role }
|
|
365
|
+
return session?.role === 'admin'
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Client-Side Hooks
|
|
372
|
+
|
|
373
|
+
Use better-auth hooks in your React components:
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
'use client'
|
|
377
|
+
|
|
378
|
+
import { authClient } from '@/lib/auth-client'
|
|
379
|
+
|
|
380
|
+
function MyComponent() {
|
|
381
|
+
const { data: session, isPending } = authClient.useSession()
|
|
382
|
+
|
|
383
|
+
if (isPending) return <div>Loading...</div>
|
|
384
|
+
|
|
385
|
+
if (!session) return <div>Not signed in</div>
|
|
386
|
+
|
|
387
|
+
return <div>Welcome, {session.user.name}!</div>
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Server-Side Session
|
|
392
|
+
|
|
393
|
+
Access the session in server components or actions:
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import { getContext } from '@/.opensaas/context'
|
|
397
|
+
|
|
398
|
+
async function myServerAction() {
|
|
399
|
+
const context = getContext()
|
|
400
|
+
|
|
401
|
+
if (!context.session) {
|
|
402
|
+
throw new Error('Not authenticated')
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Session contains fields you specified in sessionFields
|
|
406
|
+
console.log(context.session) // { userId, email, name }
|
|
407
|
+
|
|
408
|
+
// Use context.db with access control
|
|
409
|
+
const posts = await context.db.post.findMany()
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Environment Variables
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
# .env
|
|
417
|
+
DATABASE_URL=file:./dev.db
|
|
418
|
+
|
|
419
|
+
# OAuth providers (optional)
|
|
420
|
+
GITHUB_CLIENT_ID=your_github_client_id
|
|
421
|
+
GITHUB_CLIENT_SECRET=your_github_client_secret
|
|
422
|
+
GOOGLE_CLIENT_ID=your_google_client_id
|
|
423
|
+
GOOGLE_CLIENT_SECRET=your_google_client_secret
|
|
424
|
+
|
|
425
|
+
# Better-auth
|
|
426
|
+
BETTER_AUTH_SECRET=your_secret_key # Generate with: openssl rand -base64 32
|
|
427
|
+
BETTER_AUTH_URL=http://localhost:3000
|
|
428
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Examples
|
|
432
|
+
|
|
433
|
+
See `examples/auth-demo` for a complete working example.
|
|
434
|
+
|
|
435
|
+
## How It Works
|
|
436
|
+
|
|
437
|
+
1. **withAuth()** merges auth lists (User, Session, Account, Verification) with your config
|
|
438
|
+
2. **Generator** creates Prisma schema with all auth tables
|
|
439
|
+
3. **Session Provider** uses better-auth to get current session
|
|
440
|
+
4. **Context** includes session automatically in all access control functions
|
|
441
|
+
5. **UI Components** provide ready-to-use auth forms
|
|
442
|
+
|
|
443
|
+
## License
|
|
444
|
+
|
|
445
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createAuthClient } from 'better-auth/react';
|
|
2
|
+
import type { Session } from 'better-auth/types';
|
|
3
|
+
/**
|
|
4
|
+
* Create a better-auth client for use in React components
|
|
5
|
+
* This should be called once and the result exported
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // lib/auth-client.ts
|
|
10
|
+
* 'use client'
|
|
11
|
+
* import { createClient } from '@opensaas/stack-auth/client'
|
|
12
|
+
*
|
|
13
|
+
* export const authClient = createClient({
|
|
14
|
+
* baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function createClient(options: {
|
|
19
|
+
baseURL: string;
|
|
20
|
+
}): ReturnType<typeof createAuthClient>;
|
|
21
|
+
/**
|
|
22
|
+
* Re-export useful types from better-auth
|
|
23
|
+
*/
|
|
24
|
+
export type { Session };
|
|
25
|
+
/**
|
|
26
|
+
* Note: React hooks (useSession, etc.) are accessed from the client instance
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* import { authClient } from '@/lib/auth-client'
|
|
31
|
+
*
|
|
32
|
+
* function MyComponent() {
|
|
33
|
+
* const { data: session } = authClient.useSession()
|
|
34
|
+
* // ...
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAEhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAI9F;AAED;;GAEG;AACH,YAAY,EAAE,OAAO,EAAE,CAAA;AAEvB;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { createAuthClient } from 'better-auth/react';
|
|
3
|
+
/**
|
|
4
|
+
* Create a better-auth client for use in React components
|
|
5
|
+
* This should be called once and the result exported
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // lib/auth-client.ts
|
|
10
|
+
* 'use client'
|
|
11
|
+
* import { createClient } from '@opensaas/stack-auth/client'
|
|
12
|
+
*
|
|
13
|
+
* export const authClient = createClient({
|
|
14
|
+
* baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function createClient(options) {
|
|
19
|
+
return createAuthClient({
|
|
20
|
+
baseURL: options.baseURL,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAGpD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,OAAO,gBAAgB,CAAC;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { OpenSaasConfig } from '@opensaas/stack-core';
|
|
2
|
+
import type { AuthConfig, NormalizedAuthConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Normalize auth configuration with defaults
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeAuthConfig(config: AuthConfig): NormalizedAuthConfig;
|
|
7
|
+
/**
|
|
8
|
+
* Auth configuration builder
|
|
9
|
+
* Use this to create an auth configuration object
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { authConfig } from '@opensaas/stack-auth'
|
|
14
|
+
*
|
|
15
|
+
* const auth = authConfig({
|
|
16
|
+
* emailAndPassword: { enabled: true },
|
|
17
|
+
* emailVerification: { enabled: true },
|
|
18
|
+
* socialProviders: {
|
|
19
|
+
* github: { clientId: '...', clientSecret: '...' }
|
|
20
|
+
* }
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function authConfig(config: AuthConfig): AuthConfig;
|
|
25
|
+
/**
|
|
26
|
+
* Wrap an OpenSaas config with better-auth integration
|
|
27
|
+
* This merges the auth lists into the user's config and sets up session handling
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { config } from '@opensaas/stack-core'
|
|
32
|
+
* import { withAuth, authConfig } from '@opensaas/stack-auth'
|
|
33
|
+
*
|
|
34
|
+
* export default withAuth(
|
|
35
|
+
* config({
|
|
36
|
+
* db: { provider: 'sqlite', url: 'file:./dev.db' },
|
|
37
|
+
* lists: {
|
|
38
|
+
* Post: list({ ... })
|
|
39
|
+
* }
|
|
40
|
+
* }),
|
|
41
|
+
* authConfig({
|
|
42
|
+
* emailAndPassword: { enabled: true }
|
|
43
|
+
* })
|
|
44
|
+
* )
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function withAuth(opensaasConfig: OpenSaasConfig, authConfig: AuthConfig): OpenSaasConfig;
|
|
48
|
+
export type { AuthConfig, NormalizedAuthConfig };
|
|
49
|
+
export * from './types.js';
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EAIrB,MAAM,YAAY,CAAA;AAGnB;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,oBAAoB,CAuD5E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAEzD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CAAC,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,GAAG,cAAc,CAuB/F;AAED,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAA;AAChD,cAAc,YAAY,CAAA"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getAuthLists } from '../lists/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize auth configuration with defaults
|
|
4
|
+
*/
|
|
5
|
+
export function normalizeAuthConfig(config) {
|
|
6
|
+
// Email and password defaults
|
|
7
|
+
const emailAndPassword = config.emailAndPassword?.enabled
|
|
8
|
+
? {
|
|
9
|
+
enabled: true,
|
|
10
|
+
minPasswordLength: config.emailAndPassword.minPasswordLength ?? 8,
|
|
11
|
+
requireConfirmation: config.emailAndPassword.requireConfirmation ?? true,
|
|
12
|
+
}
|
|
13
|
+
: { enabled: false, minPasswordLength: 8, requireConfirmation: true };
|
|
14
|
+
// Email verification defaults
|
|
15
|
+
const emailVerification = config.emailVerification?.enabled
|
|
16
|
+
? {
|
|
17
|
+
enabled: true,
|
|
18
|
+
sendOnSignUp: config.emailVerification.sendOnSignUp ?? true,
|
|
19
|
+
tokenExpiration: config.emailVerification.tokenExpiration ?? 86400,
|
|
20
|
+
}
|
|
21
|
+
: { enabled: false, sendOnSignUp: true, tokenExpiration: 86400 };
|
|
22
|
+
// Password reset defaults
|
|
23
|
+
const passwordReset = config.passwordReset?.enabled
|
|
24
|
+
? {
|
|
25
|
+
enabled: true,
|
|
26
|
+
tokenExpiration: config.passwordReset.tokenExpiration ?? 3600,
|
|
27
|
+
}
|
|
28
|
+
: { enabled: false, tokenExpiration: 3600 };
|
|
29
|
+
// Session defaults
|
|
30
|
+
const session = {
|
|
31
|
+
expiresIn: config.session?.expiresIn || 604800, // 7 days
|
|
32
|
+
updateAge: config.session?.updateAge ?? true,
|
|
33
|
+
};
|
|
34
|
+
// Session fields defaults
|
|
35
|
+
const sessionFields = config.sessionFields || ['userId', 'email', 'name'];
|
|
36
|
+
return {
|
|
37
|
+
emailAndPassword,
|
|
38
|
+
emailVerification,
|
|
39
|
+
passwordReset,
|
|
40
|
+
socialProviders: config.socialProviders || {},
|
|
41
|
+
session,
|
|
42
|
+
sessionFields,
|
|
43
|
+
extendUserList: config.extendUserList || {},
|
|
44
|
+
sendEmail: config.sendEmail ||
|
|
45
|
+
(async ({ to, subject, html }) => {
|
|
46
|
+
console.log('[Auth] Email not sent (no sendEmail configured):');
|
|
47
|
+
console.log(`To: ${to}`);
|
|
48
|
+
console.log(`Subject: ${subject}`);
|
|
49
|
+
console.log(`Body: ${html}`);
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Auth configuration builder
|
|
55
|
+
* Use this to create an auth configuration object
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* import { authConfig } from '@opensaas/stack-auth'
|
|
60
|
+
*
|
|
61
|
+
* const auth = authConfig({
|
|
62
|
+
* emailAndPassword: { enabled: true },
|
|
63
|
+
* emailVerification: { enabled: true },
|
|
64
|
+
* socialProviders: {
|
|
65
|
+
* github: { clientId: '...', clientSecret: '...' }
|
|
66
|
+
* }
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function authConfig(config) {
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Wrap an OpenSaas config with better-auth integration
|
|
75
|
+
* This merges the auth lists into the user's config and sets up session handling
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* import { config } from '@opensaas/stack-core'
|
|
80
|
+
* import { withAuth, authConfig } from '@opensaas/stack-auth'
|
|
81
|
+
*
|
|
82
|
+
* export default withAuth(
|
|
83
|
+
* config({
|
|
84
|
+
* db: { provider: 'sqlite', url: 'file:./dev.db' },
|
|
85
|
+
* lists: {
|
|
86
|
+
* Post: list({ ... })
|
|
87
|
+
* }
|
|
88
|
+
* }),
|
|
89
|
+
* authConfig({
|
|
90
|
+
* emailAndPassword: { enabled: true }
|
|
91
|
+
* })
|
|
92
|
+
* )
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function withAuth(opensaasConfig, authConfig) {
|
|
96
|
+
const normalized = normalizeAuthConfig(authConfig);
|
|
97
|
+
// Get auth lists with user extensions
|
|
98
|
+
const authLists = getAuthLists(normalized.extendUserList);
|
|
99
|
+
// Merge auth lists with user lists (auth lists take priority)
|
|
100
|
+
const mergedLists = {
|
|
101
|
+
...opensaasConfig.lists,
|
|
102
|
+
...authLists,
|
|
103
|
+
};
|
|
104
|
+
// Return merged config with auth config attached
|
|
105
|
+
// Note: Session integration happens in the generator/context
|
|
106
|
+
const result = {
|
|
107
|
+
...opensaasConfig,
|
|
108
|
+
lists: mergedLists,
|
|
109
|
+
};
|
|
110
|
+
// Store auth config for internal use
|
|
111
|
+
result.__authConfig = normalized;
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
export * from './types.js';
|
|
115
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|