@erikey/react 0.4.30 → 0.4.32

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.
@@ -61,7 +61,8 @@ export function TwoFactorForm({
61
61
  viewPaths,
62
62
  toast,
63
63
  Link,
64
- localizeErrors
64
+ localizeErrors,
65
+ onAuthEvent
65
66
  } = useContext(AuthUIContext)
66
67
 
67
68
  localization = { ...contextLocalization, ...localization }
@@ -106,6 +107,11 @@ export function TwoFactorForm({
106
107
  setIsSubmitting?.(form.formState.isSubmitting || transitionPending)
107
108
  }, [form.formState.isSubmitting, transitionPending, setIsSubmitting])
108
109
 
110
+ // Emit two-factor start event on mount
111
+ useEffect(() => {
112
+ onAuthEvent?.({ type: "TWO_FACTOR_START" })
113
+ }, [onAuthEvent])
114
+
109
115
  // biome-ignore lint/correctness/useExhaustiveDependencies: ignore
110
116
  useEffect(() => {
111
117
  if (
@@ -162,18 +168,37 @@ export function TwoFactorForm({
162
168
  code,
163
169
  trustDevice
164
170
  }: z.infer<typeof formSchema>) {
171
+ // Emit submitted event
172
+ onAuthEvent?.({ type: "TWO_FACTOR_SUBMITTED" })
173
+
165
174
  try {
166
175
  const verifyMethod =
167
176
  method === "totp"
168
177
  ? authClient.twoFactor.verifyTotp
169
178
  : authClient.twoFactor.verifyOtp
170
179
 
171
- await verifyMethod({
180
+ const data = await verifyMethod({
172
181
  code,
173
182
  trustDevice,
174
183
  fetchOptions: { throw: true }
175
184
  })
176
185
 
186
+ // Build user and session for events
187
+ const user = (data as { user?: Record<string, unknown> }).user as {
188
+ id: string
189
+ email: string
190
+ name?: string | null
191
+ image?: string | null
192
+ emailVerified: boolean
193
+ }
194
+ const session = {
195
+ token: (data as { token?: string }).token,
196
+ user
197
+ }
198
+
199
+ onAuthEvent?.({ type: "TWO_FACTOR_SUCCESS", user, session })
200
+ onAuthEvent?.({ type: "AUTH_SUCCESS", user, session })
201
+
177
202
  await onSuccess()
178
203
 
179
204
  if (sessionData && !isTwoFactorEnabled) {
@@ -183,13 +208,24 @@ export function TwoFactorForm({
183
208
  })
184
209
  }
185
210
  } catch (error) {
211
+ const errorMessage = getLocalizedError({
212
+ error,
213
+ localization,
214
+ localizeErrors
215
+ })
216
+
217
+ const errorCode = (
218
+ error as { error?: { code?: string; message?: string } }
219
+ )?.error?.code
220
+
221
+ onAuthEvent?.({
222
+ type: "TWO_FACTOR_ERROR",
223
+ error: { message: errorMessage, code: errorCode }
224
+ })
225
+
186
226
  toast({
187
227
  variant: "error",
188
- message: getLocalizedError({
189
- error,
190
- localization,
191
- localizeErrors
192
- })
228
+ message: errorMessage
193
229
  })
194
230
 
195
231
  form.reset()
@@ -45,7 +45,8 @@ export function ProviderButton({
45
45
  social,
46
46
  genericOAuth,
47
47
  toast,
48
- localizeErrors
48
+ localizeErrors,
49
+ onAuthEvent
49
50
  } = useContext(AuthUIContext)
50
51
 
51
52
  const getRedirectTo = useCallback(
@@ -75,6 +76,9 @@ export function ProviderButton({
75
76
  const doSignInSocial = async () => {
76
77
  setIsSubmitting(true)
77
78
 
79
+ // Emit social auth start event
80
+ onAuthEvent?.({ type: "SOCIAL_AUTH_START", provider: provider.provider })
81
+
78
82
  try {
79
83
  if (other) {
80
84
  const oauth2Params = {
@@ -116,6 +120,17 @@ export function ProviderButton({
116
120
  localizeErrors
117
121
  })
118
122
 
123
+ const errorCode = (
124
+ error as { error?: { code?: string; message?: string } }
125
+ )?.error?.code
126
+
127
+ // Emit social auth error event
128
+ onAuthEvent?.({
129
+ type: "SOCIAL_AUTH_ERROR",
130
+ error: { message: errorMessage, code: errorCode },
131
+ provider: provider.provider
132
+ })
133
+
119
134
  // Call onError callback if provided (for inline display in parent)
120
135
  onError?.(errorMessage)
121
136
 
package/src/ui/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Auth forms (ALL connection types)
2
2
  export * from "./components/auth/auth-callback"
3
+ export * from "./components/auth/auth-flow"
3
4
  export * from "./components/auth/auth-form"
4
5
  export * from "./components/auth/auth-view"
5
6
  export * from "./components/auth/forms/email-otp-form"
@@ -42,5 +43,6 @@ export * from "./lib/social-providers"
42
43
  export { getViewByPath } from "./lib/utils"
43
44
  export * from "./lib/view-paths"
44
45
  export * from "./localization/auth-localization"
46
+ export * from "./types/auth-flow-events"
45
47
  export * from "./types/auth-hooks"
46
48
  export * from "./types/auth-mutators"
@@ -33,6 +33,7 @@ import type { RenderToast } from "../types/render-toast"
33
33
  import type { SignUpOptions } from "../types/sign-up-options"
34
34
  import type { SocialOptions } from "../types/social-options"
35
35
  import type { TeamOptions, TeamOptionsContext } from "../types/team-options"
36
+ import type { AuthFlowEventHandler } from "../types/auth-flow-events"
36
37
  import type { AuthViewPaths } from "./view-paths"
37
38
  import {
38
39
  accountViewPaths,
@@ -219,6 +220,11 @@ export type AuthUIContextType = {
219
220
  * Called whenever the Session changes
220
221
  */
221
222
  onSessionChange?: () => void | Promise<void>
223
+ /**
224
+ * Event handler for auth flow events.
225
+ * Called with typed events during sign-in, sign-up, verification, etc.
226
+ */
227
+ onAuthEvent?: AuthFlowEventHandler
222
228
  /**
223
229
  * Replace the current URL
224
230
  * @default navigate
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Auth Flow Event Types
3
+ *
4
+ * Typed events emitted by auth forms during the authentication flow.
5
+ * Used by AuthFlow component and can be consumed via onEvent callback.
6
+ */
7
+
8
+ /**
9
+ * Auth view states (subset of AuthViewPath for flow-relevant views)
10
+ */
11
+ export type AuthFlowView =
12
+ | "SIGN_IN"
13
+ | "SIGN_UP"
14
+ | "EMAIL_VERIFICATION"
15
+ | "TWO_FACTOR"
16
+ | "FORGOT_PASSWORD"
17
+ | "RESET_PASSWORD"
18
+ | "MAGIC_LINK"
19
+
20
+ /**
21
+ * Error information included in error events
22
+ */
23
+ export interface AuthErrorInfo {
24
+ message: string
25
+ code?: string
26
+ }
27
+
28
+ /**
29
+ * User type for events (minimal)
30
+ */
31
+ export interface AuthEventUser {
32
+ id: string
33
+ email: string
34
+ name?: string | null
35
+ image?: string | null
36
+ emailVerified: boolean
37
+ }
38
+
39
+ /**
40
+ * Session type for events (minimal)
41
+ */
42
+ export interface AuthEventSession {
43
+ token?: string
44
+ user: AuthEventUser
45
+ }
46
+
47
+ /**
48
+ * All possible auth flow events
49
+ */
50
+ export type AuthFlowEvent =
51
+ // Sign in events
52
+ | { type: "SIGN_IN_START"; email: string }
53
+ | {
54
+ type: "SIGN_IN_SUCCESS"
55
+ user: AuthEventUser
56
+ session: AuthEventSession
57
+ }
58
+ | { type: "SIGN_IN_ERROR"; error: AuthErrorInfo }
59
+ | { type: "SIGN_IN_REQUIRES_2FA"; email: string }
60
+ | { type: "SIGN_IN_REQUIRES_VERIFICATION"; email: string }
61
+
62
+ // Sign up events
63
+ | { type: "SIGN_UP_START"; email: string; name?: string }
64
+ | { type: "SIGN_UP_SUCCESS"; user: AuthEventUser }
65
+ | { type: "SIGN_UP_ERROR"; error: AuthErrorInfo }
66
+ | { type: "SIGN_UP_REQUIRES_VERIFICATION"; email: string }
67
+
68
+ // Email verification events
69
+ | {
70
+ type: "VERIFICATION_START"
71
+ email: string
72
+ method: "otp" | "link"
73
+ }
74
+ | { type: "VERIFICATION_CODE_SUBMITTED"; email: string }
75
+ | {
76
+ type: "VERIFICATION_SUCCESS"
77
+ user: AuthEventUser
78
+ session: AuthEventSession
79
+ }
80
+ | { type: "VERIFICATION_ERROR"; error: AuthErrorInfo }
81
+ | { type: "VERIFICATION_CODE_RESENT"; email: string }
82
+
83
+ // Two-factor authentication events
84
+ | { type: "TWO_FACTOR_START" }
85
+ | { type: "TWO_FACTOR_SUBMITTED" }
86
+ | {
87
+ type: "TWO_FACTOR_SUCCESS"
88
+ user: AuthEventUser
89
+ session: AuthEventSession
90
+ }
91
+ | { type: "TWO_FACTOR_ERROR"; error: AuthErrorInfo }
92
+
93
+ // Password reset events
94
+ | { type: "FORGOT_PASSWORD_START"; email: string }
95
+ | { type: "FORGOT_PASSWORD_EMAIL_SENT"; email: string }
96
+ | { type: "FORGOT_PASSWORD_ERROR"; error: AuthErrorInfo }
97
+ | { type: "PASSWORD_RESET_START" }
98
+ | { type: "PASSWORD_RESET_SUCCESS" }
99
+ | { type: "PASSWORD_RESET_ERROR"; error: AuthErrorInfo }
100
+
101
+ // Social/OAuth events
102
+ | { type: "SOCIAL_AUTH_START"; provider: string }
103
+ | {
104
+ type: "SOCIAL_AUTH_SUCCESS"
105
+ user: AuthEventUser
106
+ session: AuthEventSession
107
+ provider: string
108
+ }
109
+ | { type: "SOCIAL_AUTH_ERROR"; error: AuthErrorInfo; provider: string }
110
+
111
+ // View change events (internal navigation)
112
+ | { type: "VIEW_CHANGE"; view: AuthFlowView; email?: string }
113
+
114
+ // Final states
115
+ | {
116
+ type: "AUTH_SUCCESS"
117
+ user: AuthEventUser
118
+ session: AuthEventSession
119
+ }
120
+ | { type: "AUTH_CANCELLED" }
121
+
122
+ /**
123
+ * Event handler function type
124
+ */
125
+ export type AuthFlowEventHandler = (event: AuthFlowEvent) => void