@opensaas/stack-auth 0.31.1 → 0.33.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 (46) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +54 -0
  3. package/CLAUDE.md +42 -7
  4. package/README.md +64 -18
  5. package/dist/server/index.d.ts.map +1 -1
  6. package/dist/server/index.js +8 -2
  7. package/dist/server/index.js.map +1 -1
  8. package/dist/ui/components/ForgotPasswordForm.d.ts +10 -7
  9. package/dist/ui/components/ForgotPasswordForm.d.ts.map +1 -1
  10. package/dist/ui/components/ForgotPasswordForm.js +11 -10
  11. package/dist/ui/components/ForgotPasswordForm.js.map +1 -1
  12. package/dist/ui/components/ResetPasswordForm.d.ts +60 -0
  13. package/dist/ui/components/ResetPasswordForm.d.ts.map +1 -0
  14. package/dist/ui/components/ResetPasswordForm.js +70 -0
  15. package/dist/ui/components/ResetPasswordForm.js.map +1 -0
  16. package/dist/ui/components/SignInForm.d.ts +22 -9
  17. package/dist/ui/components/SignInForm.d.ts.map +1 -1
  18. package/dist/ui/components/SignInForm.js +25 -19
  19. package/dist/ui/components/SignInForm.js.map +1 -1
  20. package/dist/ui/components/SignUpForm.d.ts +16 -8
  21. package/dist/ui/components/SignUpForm.d.ts.map +1 -1
  22. package/dist/ui/components/SignUpForm.js +19 -23
  23. package/dist/ui/components/SignUpForm.js.map +1 -1
  24. package/dist/ui/index.d.ts +3 -0
  25. package/dist/ui/index.d.ts.map +1 -1
  26. package/dist/ui/index.js +1 -0
  27. package/dist/ui/index.js.map +1 -1
  28. package/dist/ui/lib/clean-error-message.d.ts +12 -0
  29. package/dist/ui/lib/clean-error-message.d.ts.map +1 -0
  30. package/dist/ui/lib/clean-error-message.js +20 -0
  31. package/dist/ui/lib/clean-error-message.js.map +1 -0
  32. package/dist/ui/types.d.ts +55 -0
  33. package/dist/ui/types.d.ts.map +1 -0
  34. package/dist/ui/types.js +12 -0
  35. package/dist/ui/types.js.map +1 -0
  36. package/package.json +3 -3
  37. package/src/server/index.ts +8 -2
  38. package/src/ui/components/ForgotPasswordForm.tsx +18 -14
  39. package/src/ui/components/ResetPasswordForm.tsx +182 -0
  40. package/src/ui/components/SignInForm.tsx +43 -26
  41. package/src/ui/components/SignUpForm.tsx +37 -29
  42. package/src/ui/index.ts +17 -0
  43. package/src/ui/lib/clean-error-message.ts +21 -0
  44. package/src/ui/types.ts +49 -0
  45. package/tests/clean-error-message.test.ts +29 -0
  46. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensaas/stack-auth@0.31.1 build /home/runner/work/stack/stack/packages/auth
2
+ > @opensaas/stack-auth@0.33.0 build /home/runner/work/stack/stack/packages/auth
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @opensaas/stack-auth
2
2
 
3
+ ## 0.33.0
4
+
5
+ ## 0.32.0
6
+
7
+ ### Minor Changes
8
+
9
+ - [#813](https://github.com/OpenSaasAU/stack/pull/813) [`5a6198c`](https://github.com/OpenSaasAU/stack/commit/5a6198c9489641e4b1ad542a3181c15e750f7d85) Thanks [@borisno2](https://github.com/borisno2)! - Auth forms now submit through app-owned server actions instead of the browser `authClient`
10
+
11
+ The pre-built auth forms (`SignInForm`, `SignUpForm`, `ForgotPasswordForm`, and the new
12
+ `ResetPasswordForm`) no longer take an `authClient` prop that calls `/api/auth/*` from the
13
+ browser. Instead each form takes **server action** props — `'use server'` functions the app
14
+ defines against its own `auth` instance. This keeps the auth network surface server-side and
15
+ matches the app's existing `lib/actions/*` convention. `createAuth` now auto-adds
16
+ better-auth's `nextCookies` plugin, so the session cookie set inside a server action persists.
17
+ See ADR-0020.
18
+
19
+ The package exports the action contract types (`AuthActionResult`, `SignInInput`,
20
+ `SignUpInput`, `RequestPasswordResetInput`, `ResetPasswordInput`, and the action aliases).
21
+ `createClient` is unchanged for client-side session reading (`useSession`).
22
+
23
+ Migration — define the actions in your app and pass them to the forms:
24
+
25
+ ```typescript
26
+ // lib/actions/auth.ts
27
+ 'use server'
28
+ import { headers } from 'next/headers'
29
+ import { auth } from '@/lib/auth'
30
+ import type { AuthActionResult, SignInInput } from '@opensaas/stack-auth/ui'
31
+
32
+ export async function signInAction(input: SignInInput): Promise<AuthActionResult> {
33
+ try {
34
+ await auth.api.signInEmail({
35
+ body: { email: input.email, password: input.password },
36
+ headers: await headers(),
37
+ })
38
+ return { success: true }
39
+ } catch (err) {
40
+ return { success: false, error: err instanceof Error ? err.message : 'Sign in failed' }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ```tsx
46
+ // Before
47
+ <SignInForm authClient={authClient} redirectTo="/admin" />
48
+
49
+ // After
50
+ <SignInForm signInAction={signInAction} redirectTo="/admin" />
51
+ ```
52
+
53
+ Social sign-in becomes a redirecting server action passed as `signInSocialAction`. The CLI
54
+ feature-generator now scaffolds `lib/actions/auth.ts` and a `reset-password` page, and no
55
+ longer emits `lib/auth-client.ts`.
56
+
3
57
  ## 0.31.1
4
58
 
5
59
  ## 0.31.0
package/CLAUDE.md CHANGED
@@ -38,11 +38,13 @@ Auto-generated lists:
38
38
 
39
39
  ### UI (`src/ui/index.ts`)
40
40
 
41
- Pre-built forms (client components):
41
+ Pre-built forms (client components). Each takes **server action** props (not an
42
+ `authClient`) — see "Auth forms submit through server actions" below and ADR-0020:
42
43
 
43
44
  - `SignInForm` - Email/password + OAuth sign in
44
45
  - `SignUpForm` - Create account with password confirmation
45
46
  - `ForgotPasswordForm` - Request password reset email
47
+ - `ResetPasswordForm` - Set a new password from a reset-email token
46
48
 
47
49
  ### Plugins (`src/plugins/index.ts`)
48
50
 
@@ -334,18 +336,47 @@ export default config({
334
336
  })
335
337
 
336
338
  // 2. Server (lib/auth.ts)
337
- export const auth = createAuth(config)
339
+ export const auth = createAuth(config, rawOpensaasContext)
338
340
 
339
341
  // 3. Route (app/api/auth/[...all]/route.ts)
340
342
  export { GET, POST } from '@/lib/auth'
341
343
 
342
- // 4. Client (lib/auth-client.ts)
343
- export const authClient = createClient({ baseURL: process.env.NEXT_PUBLIC_APP_URL })
344
+ // 4. Server actions (lib/actions/auth.ts) — the forms submit through these
345
+ 'use server'
346
+ import { auth } from '@/lib/auth'
347
+ import type { AuthActionResult, SignInInput } from '@opensaas/stack-auth/ui'
348
+ export async function signInAction(input: SignInInput): Promise<AuthActionResult> {
349
+ try {
350
+ await auth.api.signInEmail({ body: input, headers: await headers() })
351
+ return { success: true }
352
+ } catch (err) {
353
+ return { success: false, error: err instanceof Error ? err.message : 'Sign in failed' }
354
+ }
355
+ }
344
356
 
345
357
  // 5. UI (app/sign-in/page.tsx)
346
- <SignInForm authClient={authClient} redirectTo="/admin" />
358
+ <SignInForm signInAction={signInAction} redirectTo="/admin" />
347
359
  ```
348
360
 
361
+ ### Auth forms submit through server actions
362
+
363
+ The pre-built forms take app-owned `'use server'` action props instead of a browser
364
+ `authClient`, so the auth network surface stays server-side (no `/api/auth/*` call from
365
+ the browser). `createAuth` auto-adds better-auth's `nextCookies` plugin (as the last
366
+ plugin) so a session cookie set inside a server action persists. The action props are:
367
+
368
+ - `SignInForm`: `signInAction`, optional `signInSocialAction`
369
+ - `SignUpForm`: `signUpAction`, optional `signInSocialAction`
370
+ - `ForgotPasswordForm`: `requestPasswordResetAction`
371
+ - `ResetPasswordForm`: `resetPasswordAction` + a `token` prop (the page reads it from
372
+ `searchParams.token`)
373
+
374
+ Email actions return `AuthActionResult` and the form redirects client-side; social
375
+ sign-in redirects server-side to the provider. The package exports the contract types
376
+ (`AuthActionResult`, `SignInInput`, `SignUpInput`, `RequestPasswordResetInput`,
377
+ `ResetPasswordInput`, and the action aliases). `createClient` is unchanged for
378
+ client-side session reading (`useSession`). See ADR-0020 and `examples/starter-auth`.
379
+
349
380
  ### Access Control with Session
350
381
 
351
382
  ```typescript
@@ -376,8 +407,12 @@ authPlugin({
376
407
  }
377
408
  })
378
409
 
379
- // UI
380
- <SignInForm socialProviders={['github', 'google']} />
410
+ // UI — pass the redirecting social action to enable the provider buttons
411
+ <SignInForm
412
+ signInAction={signInAction}
413
+ signInSocialAction={signInSocialAction}
414
+ socialProviders={['github', 'google']}
415
+ />
381
416
  ```
382
417
 
383
418
  ## Type Safety
package/README.md CHANGED
@@ -77,17 +77,30 @@ export const POST = auth.handler
77
77
  export { GET, POST } from '@/lib/auth'
78
78
  ```
79
79
 
80
- ### 5. Create Auth Client
80
+ ### 5. Create Auth Server Actions
81
81
 
82
- ```typescript
83
- // lib/auth-client.ts
84
- 'use client'
85
-
86
- import { createClient } from '@opensaas/stack-auth/client'
82
+ The pre-built forms submit through app-owned server actions (calling better-auth's
83
+ server API directly) instead of a browser client. `createAuth` auto-adds better-auth's
84
+ `nextCookies` plugin, so the session cookie set inside these actions persists.
87
85
 
88
- export const authClient = createClient({
89
- baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
90
- })
86
+ ```typescript
87
+ // lib/actions/auth.ts
88
+ 'use server'
89
+
90
+ import { headers } from 'next/headers'
91
+ import { auth } from '@/lib/auth'
92
+ import type { AuthActionResult, SignInInput } from '@opensaas/stack-auth/ui'
93
+
94
+ export async function signInAction(input: SignInInput): Promise<AuthActionResult> {
95
+ try {
96
+ await auth.api.signInEmail({ body: input, headers: await headers() })
97
+ return { success: true }
98
+ } catch (err) {
99
+ return { success: false, error: err instanceof Error ? err.message : 'Sign in failed' }
100
+ }
101
+ }
102
+ // ...and signUpAction / requestPasswordResetAction / resetPasswordAction — see
103
+ // examples/starter-auth/lib/actions/auth.ts for the full set.
91
104
  ```
92
105
 
93
106
  ### 6. Add Sign In Page
@@ -95,12 +108,12 @@ export const authClient = createClient({
95
108
  ```typescript
96
109
  // app/sign-in/page.tsx
97
110
  import { SignInForm } from '@opensaas/stack-auth/ui'
98
- import { authClient } from '@/lib/auth-client'
111
+ import { signInAction } from '@/lib/actions/auth'
99
112
 
100
113
  export default function SignInPage() {
101
114
  return (
102
115
  <div className="min-h-screen flex items-center justify-center">
103
- <SignInForm authClient={authClient} redirectTo="/admin" />
116
+ <SignInForm signInAction={signInAction} redirectTo="/admin" />
104
117
  </div>
105
118
  )
106
119
  }
@@ -238,14 +251,18 @@ the full migrator walkthrough and a Schema-parity (clean-diff) check.
238
251
 
239
252
  ## UI Components
240
253
 
254
+ Each form takes app-owned server action props (defined in `lib/actions/auth.ts`),
255
+ not an `authClient`. See ADR-0020 and `examples/starter-auth`.
256
+
241
257
  ### SignInForm
242
258
 
243
259
  ```typescript
244
260
  import { SignInForm } from '@opensaas/stack-auth/ui'
245
- import { authClient } from '@/lib/auth-client'
261
+ import { signInAction, signInSocialAction } from '@/lib/actions/auth'
246
262
 
247
263
  <SignInForm
248
- authClient={authClient}
264
+ signInAction={signInAction}
265
+ signInSocialAction={signInSocialAction}
249
266
  redirectTo="/dashboard"
250
267
  showSocialProviders={true}
251
268
  socialProviders={['github', 'google']}
@@ -258,10 +275,10 @@ import { authClient } from '@/lib/auth-client'
258
275
 
259
276
  ```typescript
260
277
  import { SignUpForm } from '@opensaas/stack-auth/ui'
261
- import { authClient } from '@/lib/auth-client'
278
+ import { signUpAction } from '@/lib/actions/auth'
262
279
 
263
280
  <SignUpForm
264
- authClient={authClient}
281
+ signUpAction={signUpAction}
265
282
  redirectTo="/dashboard"
266
283
  requirePasswordConfirmation={true}
267
284
  onSuccess={() => console.log('Account created!')}
@@ -272,14 +289,31 @@ import { authClient } from '@/lib/auth-client'
272
289
 
273
290
  ```typescript
274
291
  import { ForgotPasswordForm } from '@opensaas/stack-auth/ui'
275
- import { authClient } from '@/lib/auth-client'
292
+ import { requestPasswordResetAction } from '@/lib/actions/auth'
276
293
 
277
294
  <ForgotPasswordForm
278
- authClient={authClient}
295
+ requestPasswordResetAction={requestPasswordResetAction}
279
296
  onSuccess={() => console.log('Reset email sent!')}
280
297
  />
281
298
  ```
282
299
 
300
+ ### ResetPasswordForm
301
+
302
+ ```typescript
303
+ // app/reset-password/page.tsx
304
+ import { ResetPasswordForm } from '@opensaas/stack-auth/ui'
305
+ import { resetPasswordAction } from '@/lib/actions/auth'
306
+
307
+ export default async function ResetPasswordPage({
308
+ searchParams,
309
+ }: {
310
+ searchParams: Promise<{ token?: string }>
311
+ }) {
312
+ const { token } = await searchParams
313
+ return <ResetPasswordForm resetPasswordAction={resetPasswordAction} token={token ?? ''} />
314
+ }
315
+ ```
316
+
283
317
  ## Auto-Generated Lists
284
318
 
285
319
  The following lists are automatically created when you use `authPlugin()`:
@@ -404,7 +438,19 @@ access: {
404
438
 
405
439
  ## Client-Side Hooks
406
440
 
407
- Use better-auth hooks in your React components:
441
+ The auth forms no longer need a browser client (they use server actions). To read the
442
+ session on the client, create a client yourself with `createClient` and use its hooks:
443
+
444
+ ```typescript
445
+ // lib/auth-client.ts
446
+ 'use client'
447
+
448
+ import { createClient } from '@opensaas/stack-auth/client'
449
+
450
+ export const authClient = createClient({
451
+ baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
452
+ })
453
+ ```
408
454
 
409
455
  ```typescript
410
456
  'use client'
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAgCzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,iDAyIhD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,EACnC,aAAa,EAAE,MAAM,EAAE,2CA0BxB;AAED,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAgCzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,iDA8IhD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,EACnC,aAAa,EAAE,MAAM,EAAE,2CA0BxB;AAED,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { betterAuth } from 'better-auth';
2
2
  import { prismaAdapter } from 'better-auth/adapters/prisma';
3
+ import { nextCookies } from 'better-auth/next-js';
3
4
  /**
4
5
  * Get better-auth database configuration from OpenSaas config
5
6
  */
@@ -101,8 +102,13 @@ export function createAuth(opensaasConfig, context) {
101
102
  max: authConfig.rateLimit.max,
102
103
  }
103
104
  : undefined,
104
- // Pass through any additional Better Auth plugins
105
- plugins: authConfig.betterAuthPlugins || [],
105
+ // Pass through any additional Better Auth plugins, then append
106
+ // nextCookies LAST so it can write the Set-Cookie headers produced by
107
+ // any auth.api.* call made inside a Next.js server action into Next's
108
+ // cookie store. This is what makes the server-action auth forms (which
109
+ // call auth.api.signInEmail/signUpEmail/etc. server-side) actually
110
+ // persist a session. It must be the final plugin in the array.
111
+ plugins: [...(authConfig.betterAuthPlugins || []), nextCookies()],
106
112
  };
107
113
  authInstance = betterAuth(betterAuthConfig);
108
114
  return authInstance;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import type { createAuthClient } from 'better-auth/react';
1
+ import type { RequestPasswordResetAction } from '../types.js';
2
2
  export type ForgotPasswordFormProps = {
3
3
  /**
4
- * Better-auth client instance
5
- * Created with createAuthClient from better-auth/react
4
+ * Server action that requests a password-reset email.
5
+ * Define it in your app (`'use server'`) against your own auth instance.
6
6
  */
7
- authClient: ReturnType<typeof createAuthClient>;
7
+ requestPasswordResetAction: RequestPasswordResetAction;
8
8
  /**
9
9
  * Custom CSS class for the form container
10
10
  */
@@ -22,15 +22,18 @@ export type ForgotPasswordFormProps = {
22
22
  * Forgot password form component
23
23
  * Allows users to request a password reset email
24
24
  *
25
+ * Submits through an app-owned server action rather than calling the auth API
26
+ * from the browser. See the "Auth action" contract in `@opensaas/stack-auth/ui`.
27
+ *
25
28
  * @example
26
29
  * ```typescript
27
30
  * import { ForgotPasswordForm } from '@opensaas/stack-auth/ui'
28
- * import { authClient } from '@/lib/auth-client'
31
+ * import { requestPasswordResetAction } from '@/lib/actions/auth'
29
32
  *
30
33
  * export default function ForgotPasswordPage() {
31
- * return <ForgotPasswordForm authClient={authClient} />
34
+ * return <ForgotPasswordForm requestPasswordResetAction={requestPasswordResetAction} />
32
35
  * }
33
36
  * ```
34
37
  */
35
- export declare function ForgotPasswordForm({ authClient, className, onSuccess, onError, }: ForgotPasswordFormProps): import("react/jsx-runtime").JSX.Element;
38
+ export declare function ForgotPasswordForm({ requestPasswordResetAction, className, onSuccess, onError, }: ForgotPasswordFormProps): import("react/jsx-runtime").JSX.Element;
36
39
  //# sourceMappingURL=ForgotPasswordForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ForgotPasswordForm.d.ts","sourceRoot":"","sources":["../../../src/ui/components/ForgotPasswordForm.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEzD,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;OAGG;IACH,UAAU,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAA;IAC/C;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,UAAU,EACV,SAAc,EACd,SAAS,EACT,OAAO,GACR,EAAE,uBAAuB,2CA2EzB"}
1
+ {"version":3,"file":"ForgotPasswordForm.d.ts","sourceRoot":"","sources":["../../../src/ui/components/ForgotPasswordForm.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AAE7D,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;OAGG;IACH,0BAA0B,EAAE,0BAA0B,CAAA;IACtD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,0BAA0B,EAC1B,SAAc,EACd,SAAS,EACT,OAAO,GACR,EAAE,uBAAuB,2CA2EzB"}
@@ -1,21 +1,25 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from 'react';
4
+ import { cleanAuthErrorMessage } from '../lib/clean-error-message.js';
4
5
  /**
5
6
  * Forgot password form component
6
7
  * Allows users to request a password reset email
7
8
  *
9
+ * Submits through an app-owned server action rather than calling the auth API
10
+ * from the browser. See the "Auth action" contract in `@opensaas/stack-auth/ui`.
11
+ *
8
12
  * @example
9
13
  * ```typescript
10
14
  * import { ForgotPasswordForm } from '@opensaas/stack-auth/ui'
11
- * import { authClient } from '@/lib/auth-client'
15
+ * import { requestPasswordResetAction } from '@/lib/actions/auth'
12
16
  *
13
17
  * export default function ForgotPasswordPage() {
14
- * return <ForgotPasswordForm authClient={authClient} />
18
+ * return <ForgotPasswordForm requestPasswordResetAction={requestPasswordResetAction} />
15
19
  * }
16
20
  * ```
17
21
  */
18
- export function ForgotPasswordForm({ authClient, className = '', onSuccess, onError, }) {
22
+ export function ForgotPasswordForm({ requestPasswordResetAction, className = '', onSuccess, onError, }) {
19
23
  const [email, setEmail] = useState('');
20
24
  const [error, setError] = useState('');
21
25
  const [success, setSuccess] = useState(false);
@@ -26,18 +30,15 @@ export function ForgotPasswordForm({ authClient, className = '', onSuccess, onEr
26
30
  setSuccess(false);
27
31
  setLoading(true);
28
32
  try {
29
- const result = await authClient.requestPasswordReset({
30
- email,
31
- redirectTo: '/reset-password',
32
- });
33
- if (result.error) {
34
- throw new Error(result.error.message);
33
+ const result = await requestPasswordResetAction({ email });
34
+ if (!result.success) {
35
+ throw new Error(cleanAuthErrorMessage(result.error, 'Failed to send reset email'));
35
36
  }
36
37
  setSuccess(true);
37
38
  onSuccess?.();
38
39
  }
39
40
  catch (err) {
40
- const message = err instanceof Error ? err.message : 'Failed to send reset email';
41
+ const message = cleanAuthErrorMessage(err instanceof Error ? err.message : undefined, 'Failed to send reset email');
41
42
  setError(message);
42
43
  onError?.(err instanceof Error ? err : new Error(message));
43
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ForgotPasswordForm.js","sourceRoot":"","sources":["../../../src/ui/components/ForgotPasswordForm.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAuBvC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,UAAU,EACV,SAAS,GAAG,EAAE,EACd,SAAS,EACT,OAAO,GACiB;IACxB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IACtC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IACtC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE7C,MAAM,YAAY,GAAG,KAAK,EAAE,CAAkB,EAAE,EAAE;QAChD,CAAC,CAAC,cAAc,EAAE,CAAA;QAClB,QAAQ,CAAC,EAAE,CAAC,CAAA;QACZ,UAAU,CAAC,KAAK,CAAC,CAAA;QACjB,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,oBAAoB,CAAC;gBACnD,KAAK;gBACL,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAA;YAEF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACvC,CAAC;YAED,UAAU,CAAC,IAAI,CAAC,CAAA;YAChB,SAAS,EAAE,EAAE,CAAA;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAA;YACjF,QAAQ,CAAC,OAAO,CAAC,CAAA;YACjB,OAAO,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;QAC5D,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAA;QACnB,CAAC;IACH,CAAC,CAAA;IAED,OAAO,CACL,eAAK,SAAS,EAAE,+BAA+B,SAAS,EAAE,aACxD,aAAI,SAAS,EAAC,yBAAyB,gCAAqB,EAE3D,KAAK,IAAI,CACR,cAAK,SAAS,EAAC,qEAAqE,YACjF,KAAK,GACF,CACP,EAEA,OAAO,IAAI,CACV,cAAK,SAAS,EAAC,2EAA2E,8EAEpF,CACP,EAED,gBAAM,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAC,WAAW,aACjD,0BACE,gBAAO,OAAO,EAAC,OAAO,EAAC,SAAS,EAAC,gCAAgC,sBAEzD,EACR,gBACE,EAAE,EAAC,OAAO,EACV,IAAI,EAAC,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAE,CAAC,CAAC,MAA2B,CAAC,KAAK,CAAC,EAC/D,QAAQ,QACR,SAAS,EAAC,wGAAwG,EAClH,QAAQ,EAAE,OAAO,IAAI,OAAO,GAC5B,IACE,EAEN,iBACE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,OAAO,IAAI,OAAO,EAC5B,SAAS,EAAC,uHAAuH,YAEhI,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,iBAAiB,GAC7D,IACJ,IACH,CACP,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"ForgotPasswordForm.js","sourceRoot":"","sources":["../../../src/ui/components/ForgotPasswordForm.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { ResetPasswordAction } from '../types.js';
2
+ export type ResetPasswordFormProps = {
3
+ /**
4
+ * Server action that completes the password reset.
5
+ * Define it in your app (`'use server'`) against your own auth instance.
6
+ */
7
+ resetPasswordAction: ResetPasswordAction;
8
+ /**
9
+ * The reset token from the email link. The page reads it from
10
+ * `searchParams.token` and passes it in. When empty, the form renders an
11
+ * "invalid or expired link" state instead of a password form.
12
+ */
13
+ token: string;
14
+ /**
15
+ * URL to redirect to after a successful reset
16
+ * @default '/sign-in'
17
+ */
18
+ redirectTo?: string;
19
+ /**
20
+ * Require password confirmation
21
+ * @default true
22
+ */
23
+ requirePasswordConfirmation?: boolean;
24
+ /**
25
+ * Custom CSS class for the form container
26
+ */
27
+ className?: string;
28
+ /**
29
+ * Callback when the reset succeeds
30
+ */
31
+ onSuccess?: () => void;
32
+ /**
33
+ * Callback when the reset fails
34
+ */
35
+ onError?: (error: Error) => void;
36
+ };
37
+ /**
38
+ * Reset password form component
39
+ * Completes a password reset using the token from the reset email.
40
+ *
41
+ * Submits through an app-owned server action rather than calling the auth API
42
+ * from the browser. See the "Auth action" contract in `@opensaas/stack-auth/ui`.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { ResetPasswordForm } from '@opensaas/stack-auth/ui'
47
+ * import { resetPasswordAction } from '@/lib/actions/auth'
48
+ *
49
+ * export default async function ResetPasswordPage({
50
+ * searchParams,
51
+ * }: {
52
+ * searchParams: Promise<{ token?: string }>
53
+ * }) {
54
+ * const { token } = await searchParams
55
+ * return <ResetPasswordForm resetPasswordAction={resetPasswordAction} token={token ?? ''} />
56
+ * }
57
+ * ```
58
+ */
59
+ export declare function ResetPasswordForm({ resetPasswordAction, token, redirectTo, requirePasswordConfirmation, className, onSuccess, onError, }: ResetPasswordFormProps): import("react/jsx-runtime").JSX.Element;
60
+ //# sourceMappingURL=ResetPasswordForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResetPasswordForm.d.ts","sourceRoot":"","sources":["../../../src/ui/components/ResetPasswordForm.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEtD,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,mBAAmB,EAAE,mBAAmB,CAAA;IACxC;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;CACjC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,mBAAmB,EACnB,KAAK,EACL,UAAuB,EACvB,2BAAkC,EAClC,SAAc,EACd,SAAS,EACT,OAAO,GACR,EAAE,sBAAsB,2CA4GxB"}
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { useRouter } from 'next/navigation.js';
5
+ import { cleanAuthErrorMessage } from '../lib/clean-error-message.js';
6
+ /**
7
+ * Reset password form component
8
+ * Completes a password reset using the token from the reset email.
9
+ *
10
+ * Submits through an app-owned server action rather than calling the auth API
11
+ * from the browser. See the "Auth action" contract in `@opensaas/stack-auth/ui`.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { ResetPasswordForm } from '@opensaas/stack-auth/ui'
16
+ * import { resetPasswordAction } from '@/lib/actions/auth'
17
+ *
18
+ * export default async function ResetPasswordPage({
19
+ * searchParams,
20
+ * }: {
21
+ * searchParams: Promise<{ token?: string }>
22
+ * }) {
23
+ * const { token } = await searchParams
24
+ * return <ResetPasswordForm resetPasswordAction={resetPasswordAction} token={token ?? ''} />
25
+ * }
26
+ * ```
27
+ */
28
+ export function ResetPasswordForm({ resetPasswordAction, token, redirectTo = '/sign-in', requirePasswordConfirmation = true, className = '', onSuccess, onError, }) {
29
+ const router = useRouter();
30
+ const [password, setPassword] = useState('');
31
+ const [confirmPassword, setConfirmPassword] = useState('');
32
+ const [error, setError] = useState('');
33
+ const [loading, setLoading] = useState(false);
34
+ // Guard: no token means the user hit this page directly or followed a
35
+ // malformed/expired link. Don't render a form that can't succeed.
36
+ if (!token) {
37
+ return (_jsxs("div", { className: `w-full max-w-md mx-auto p-6 ${className}`, children: [_jsx("h2", { className: "text-2xl font-bold mb-6", children: "Reset Password" }), _jsx("div", { className: "bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded", children: "This password reset link is invalid or has expired. Please request a new one." })] }));
38
+ }
39
+ const handleSubmit = async (e) => {
40
+ e.preventDefault();
41
+ setError('');
42
+ if (requirePasswordConfirmation && password !== confirmPassword) {
43
+ setError('Passwords do not match');
44
+ return;
45
+ }
46
+ setLoading(true);
47
+ try {
48
+ const result = await resetPasswordAction({ token, password });
49
+ if (!result.success) {
50
+ throw new Error(cleanAuthErrorMessage(result.error, 'Failed to reset password'));
51
+ }
52
+ if (onSuccess) {
53
+ onSuccess();
54
+ }
55
+ else {
56
+ router.push(redirectTo);
57
+ }
58
+ }
59
+ catch (err) {
60
+ const message = cleanAuthErrorMessage(err instanceof Error ? err.message : undefined, 'Failed to reset password');
61
+ setError(message);
62
+ onError?.(err instanceof Error ? err : new Error(message));
63
+ }
64
+ finally {
65
+ setLoading(false);
66
+ }
67
+ };
68
+ return (_jsxs("div", { className: `w-full max-w-md mx-auto p-6 ${className}`, children: [_jsx("h2", { className: "text-2xl font-bold mb-6", children: "Reset Password" }), error && (_jsx("div", { className: "bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded mb-4", children: error })), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "password", className: "block text-sm font-medium mb-2", children: "New Password" }), _jsx("input", { id: "password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500", disabled: loading })] }), requirePasswordConfirmation && (_jsxs("div", { children: [_jsx("label", { htmlFor: "confirmPassword", className: "block text-sm font-medium mb-2", children: "Confirm New Password" }), _jsx("input", { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500", disabled: loading })] })), _jsx("button", { type: "submit", disabled: loading, className: "w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed", children: loading ? 'Resetting...' : 'Reset Password' })] })] }));
69
+ }
70
+ //# sourceMappingURL=ResetPasswordForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResetPasswordForm.js","sourceRoot":"","sources":["../../../src/ui/components/ResetPasswordForm.tsx"],"names":[],"mappings":"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"}