@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.
- package/dist/styles.css +5 -0
- package/dist/styles.css.map +1 -1
- package/dist/ui/index.mjs +6122 -5832
- package/dist/ui/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/ui/components/auth/auth-flow.tsx +254 -0
- package/src/ui/components/auth/forms/email-verification-form.tsx +151 -7
- package/src/ui/components/auth/forms/sign-in-form.tsx +33 -6
- package/src/ui/components/auth/forms/sign-up-form.tsx +60 -4
- package/src/ui/components/auth/forms/two-factor-form.tsx +43 -7
- package/src/ui/components/auth/provider-button.tsx +16 -1
- package/src/ui/index.ts +2 -0
- package/src/ui/lib/auth-ui-provider.tsx +6 -0
- package/src/ui/types/auth-flow-events.ts +125 -0
|
@@ -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:
|
|
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
|