@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.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/INTEGRATION_SUMMARY.md +425 -0
  3. package/README.md +445 -0
  4. package/dist/client/index.d.ts +38 -0
  5. package/dist/client/index.d.ts.map +1 -0
  6. package/dist/client/index.js +23 -0
  7. package/dist/client/index.js.map +1 -0
  8. package/dist/config/index.d.ts +50 -0
  9. package/dist/config/index.d.ts.map +1 -0
  10. package/dist/config/index.js +115 -0
  11. package/dist/config/index.js.map +1 -0
  12. package/dist/config/types.d.ts +160 -0
  13. package/dist/config/types.d.ts.map +1 -0
  14. package/dist/config/types.js +2 -0
  15. package/dist/config/types.js.map +1 -0
  16. package/dist/index.d.ts +35 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +34 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/lists/index.d.ts +46 -0
  21. package/dist/lists/index.d.ts.map +1 -0
  22. package/dist/lists/index.js +227 -0
  23. package/dist/lists/index.js.map +1 -0
  24. package/dist/server/index.d.ts +27 -0
  25. package/dist/server/index.d.ts.map +1 -0
  26. package/dist/server/index.js +90 -0
  27. package/dist/server/index.js.map +1 -0
  28. package/dist/ui/components/ForgotPasswordForm.d.ts +36 -0
  29. package/dist/ui/components/ForgotPasswordForm.d.ts.map +1 -0
  30. package/dist/ui/components/ForgotPasswordForm.js +50 -0
  31. package/dist/ui/components/ForgotPasswordForm.js.map +1 -0
  32. package/dist/ui/components/SignInForm.d.ts +52 -0
  33. package/dist/ui/components/SignInForm.d.ts.map +1 -0
  34. package/dist/ui/components/SignInForm.js +66 -0
  35. package/dist/ui/components/SignInForm.js.map +1 -0
  36. package/dist/ui/components/SignUpForm.d.ts +56 -0
  37. package/dist/ui/components/SignUpForm.d.ts.map +1 -0
  38. package/dist/ui/components/SignUpForm.js +74 -0
  39. package/dist/ui/components/SignUpForm.js.map +1 -0
  40. package/dist/ui/index.d.ts +7 -0
  41. package/dist/ui/index.d.ts.map +1 -0
  42. package/dist/ui/index.js +4 -0
  43. package/dist/ui/index.js.map +1 -0
  44. package/package.json +55 -0
  45. package/src/client/index.ts +44 -0
  46. package/src/config/index.ts +140 -0
  47. package/src/config/types.ts +166 -0
  48. package/src/index.ts +44 -0
  49. package/src/lists/index.ts +245 -0
  50. package/src/server/index.ts +120 -0
  51. package/src/ui/components/ForgotPasswordForm.tsx +120 -0
  52. package/src/ui/components/SignInForm.tsx +191 -0
  53. package/src/ui/components/SignUpForm.tsx +238 -0
  54. package/src/ui/index.ts +7 -0
  55. package/tsconfig.json +14 -0
  56. 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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"}