better-auth-ui 3.2.5
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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/auth-hooks-IOEvlYYv.d.cts +6966 -0
- package/dist/auth-hooks-IOEvlYYv.d.ts +6966 -0
- package/dist/auth-mutators-DdqOmQ32.d.cts +29 -0
- package/dist/auth-mutators-DdqOmQ32.d.ts +29 -0
- package/dist/auth-ui-provider-BsH3xJDw.d.ts +697 -0
- package/dist/auth-ui-provider-DhZfncd3.d.cts +697 -0
- package/dist/chunk-BDFQSFBU.js +750 -0
- package/dist/chunk-CRAHKL2C.cjs +801 -0
- package/dist/chunk-MJPOA6PK.js +801 -0
- package/dist/chunk-SV64DXGW.cjs +750 -0
- package/dist/index.cjs +12618 -0
- package/dist/index.d.cts +771 -0
- package/dist/index.d.ts +771 -0
- package/dist/index.js +12618 -0
- package/dist/instantdb.cjs +189 -0
- package/dist/instantdb.d.cts +36 -0
- package/dist/instantdb.d.ts +36 -0
- package/dist/instantdb.js +189 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/dist/server.cjs +194 -0
- package/dist/server.d.cts +35 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.js +194 -0
- package/dist/style.css +1 -0
- package/dist/tanstack.cjs +153 -0
- package/dist/tanstack.d.cts +18 -0
- package/dist/tanstack.d.ts +18 -0
- package/dist/tanstack.js +153 -0
- package/dist/triplit.cjs +201 -0
- package/dist/triplit.d.cts +31 -0
- package/dist/triplit.d.ts +31 -0
- package/dist/triplit.js +201 -0
- package/dist/utils-C5R37WDe.d.cts +3 -0
- package/dist/utils-C5R37WDe.d.ts +3 -0
- package/dist/view-paths-CHSJf5dv.d.cts +645 -0
- package/dist/view-paths-CHSJf5dv.d.ts +645 -0
- package/package.json +156 -0
- package/src/components/account/account-view.tsx +220 -0
- package/src/components/auth/auth-callback.tsx +36 -0
- package/src/components/auth/auth-form.tsx +277 -0
- package/src/components/auth/auth-view.tsx +389 -0
- package/src/components/auth/email-otp-button.tsx +53 -0
- package/src/components/auth/forms/email-otp-form.tsx +288 -0
- package/src/components/auth/forms/forgot-password-form.tsx +168 -0
- package/src/components/auth/forms/magic-link-form.tsx +191 -0
- package/src/components/auth/forms/recover-account-form.tsx +138 -0
- package/src/components/auth/forms/reset-password-form.tsx +215 -0
- package/src/components/auth/forms/sign-in-form.tsx +289 -0
- package/src/components/auth/forms/sign-up-form.tsx +788 -0
- package/src/components/auth/forms/two-factor-form.tsx +372 -0
- package/src/components/auth/magic-link-button.tsx +54 -0
- package/src/components/auth/one-tap.tsx +48 -0
- package/src/components/auth/otp-input-group.tsx +65 -0
- package/src/components/auth/passkey-button.tsx +85 -0
- package/src/components/auth/provider-button.tsx +141 -0
- package/src/components/auth/sign-out.tsx +25 -0
- package/src/components/auth-loading.tsx +21 -0
- package/src/components/captcha/captcha.tsx +79 -0
- package/src/components/captcha/recaptcha-badge.tsx +61 -0
- package/src/components/captcha/recaptcha-v2.tsx +58 -0
- package/src/components/captcha/recaptcha-v3.tsx +73 -0
- package/src/components/email/email-template.tsx +216 -0
- package/src/components/form-error.tsx +27 -0
- package/src/components/organization/accept-invitation-card.tsx +362 -0
- package/src/components/organization/create-organization-dialog.tsx +395 -0
- package/src/components/organization/delete-organization-card.tsx +101 -0
- package/src/components/organization/delete-organization-dialog.tsx +209 -0
- package/src/components/organization/invitation-cell.tsx +156 -0
- package/src/components/organization/invite-member-dialog.tsx +258 -0
- package/src/components/organization/leave-organization-dialog.tsx +150 -0
- package/src/components/organization/member-cell.tsx +187 -0
- package/src/components/organization/organization-cell-view.tsx +122 -0
- package/src/components/organization/organization-cell.tsx +154 -0
- package/src/components/organization/organization-invitations-card.tsx +94 -0
- package/src/components/organization/organization-logo-card.tsx +308 -0
- package/src/components/organization/organization-logo.tsx +120 -0
- package/src/components/organization/organization-members-card.tsx +155 -0
- package/src/components/organization/organization-name-card.tsx +204 -0
- package/src/components/organization/organization-settings-cards.tsx +67 -0
- package/src/components/organization/organization-slug-card.tsx +223 -0
- package/src/components/organization/organization-switcher.tsx +512 -0
- package/src/components/organization/organization-view.tsx +228 -0
- package/src/components/organization/organizations-card.tsx +72 -0
- package/src/components/organization/personal-account-view.tsx +115 -0
- package/src/components/organization/remove-member-dialog.tsx +144 -0
- package/src/components/organization/update-member-role-dialog.tsx +213 -0
- package/src/components/organization/user-invitations-card.tsx +238 -0
- package/src/components/password-input.tsx +56 -0
- package/src/components/provider-icons.tsx +385 -0
- package/src/components/redirect-to-sign-in.tsx +16 -0
- package/src/components/redirect-to-sign-up.tsx +16 -0
- package/src/components/settings/account/account-cell.tsx +158 -0
- package/src/components/settings/account/accounts-card.tsx +75 -0
- package/src/components/settings/account/delete-account-card.tsx +65 -0
- package/src/components/settings/account/delete-account-dialog.tsx +231 -0
- package/src/components/settings/account/update-avatar-card.tsx +198 -0
- package/src/components/settings/account/update-field-card.tsx +282 -0
- package/src/components/settings/account/update-name-card.tsx +39 -0
- package/src/components/settings/account/update-username-card.tsx +42 -0
- package/src/components/settings/account-settings-cards.tsx +123 -0
- package/src/components/settings/api-key/api-key-cell.tsx +108 -0
- package/src/components/settings/api-key/api-key-delete-dialog.tsx +162 -0
- package/src/components/settings/api-key/api-key-display-dialog.tsx +110 -0
- package/src/components/settings/api-key/api-keys-card.tsx +98 -0
- package/src/components/settings/api-key/create-api-key-dialog.tsx +376 -0
- package/src/components/settings/passkey/passkey-cell.tsx +113 -0
- package/src/components/settings/passkey/passkeys-card.tsx +111 -0
- package/src/components/settings/providers/provider-cell.tsx +152 -0
- package/src/components/settings/providers/providers-card.tsx +108 -0
- package/src/components/settings/security/change-email-card.tsx +200 -0
- package/src/components/settings/security/change-password-card.tsx +326 -0
- package/src/components/settings/security/session-cell.tsx +120 -0
- package/src/components/settings/security/sessions-card.tsx +58 -0
- package/src/components/settings/security-settings-cards.tsx +111 -0
- package/src/components/settings/shared/session-freshness-dialog.tsx +97 -0
- package/src/components/settings/shared/settings-action-button.tsx +51 -0
- package/src/components/settings/shared/settings-card-footer.tsx +94 -0
- package/src/components/settings/shared/settings-card-header.tsx +67 -0
- package/src/components/settings/shared/settings-card.tsx +106 -0
- package/src/components/settings/skeletons/input-field-skeleton.tsx +18 -0
- package/src/components/settings/skeletons/settings-cell-skeleton.tsx +37 -0
- package/src/components/settings/two-factor/backup-codes-dialog.tsx +113 -0
- package/src/components/settings/two-factor/two-factor-card.tsx +63 -0
- package/src/components/settings/two-factor/two-factor-password-dialog.tsx +226 -0
- package/src/components/signed-in.tsx +20 -0
- package/src/components/signed-out.tsx +20 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input-otp.tsx +77 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/select.tsx +185 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/user-avatar.tsx +147 -0
- package/src/components/user-button.tsx +409 -0
- package/src/components/user-view.tsx +138 -0
- package/src/hooks/use-auth-data.ts +184 -0
- package/src/hooks/use-authenticate.ts +62 -0
- package/src/hooks/use-captcha.tsx +138 -0
- package/src/hooks/use-current-organization.ts +59 -0
- package/src/hooks/use-hydrated.ts +13 -0
- package/src/hooks/use-lang.ts +32 -0
- package/src/hooks/use-success-transition.ts +51 -0
- package/src/hooks/use-theme.ts +39 -0
- package/src/index.ts +65 -0
- package/src/instantdb.ts +1 -0
- package/src/lib/auth-data-cache.ts +90 -0
- package/src/lib/auth-ui-provider.tsx +658 -0
- package/src/lib/gravatar-utils.ts +58 -0
- package/src/lib/image-utils.ts +55 -0
- package/src/lib/instantdb/model-names.ts +24 -0
- package/src/lib/instantdb/use-instant-options.ts +98 -0
- package/src/lib/instantdb/use-list-accounts.ts +38 -0
- package/src/lib/instantdb/use-list-sessions.ts +53 -0
- package/src/lib/instantdb/use-session.ts +55 -0
- package/src/lib/organization-refetcher.tsx +56 -0
- package/src/lib/social-providers.ts +144 -0
- package/src/lib/tanstack/auth-ui-provider-tanstack.tsx +49 -0
- package/src/lib/tanstack/use-tanstack-options.ts +112 -0
- package/src/lib/triplit/model-names.ts +24 -0
- package/src/lib/triplit/use-conditional-query.ts +82 -0
- package/src/lib/triplit/use-list-accounts.ts +31 -0
- package/src/lib/triplit/use-list-sessions.ts +33 -0
- package/src/lib/triplit/use-session.ts +42 -0
- package/src/lib/triplit/use-triplit-hooks.ts +68 -0
- package/src/lib/triplit/use-triplit-token.ts +44 -0
- package/src/lib/utils.ts +105 -0
- package/src/lib/view-paths.ts +55 -0
- package/src/localization/admin-error-codes.ts +20 -0
- package/src/localization/anonymous-error-codes.ts +6 -0
- package/src/localization/api-key-error-codes.ts +32 -0
- package/src/localization/auth-localization.ts +740 -0
- package/src/localization/base-error-codes.ts +27 -0
- package/src/localization/captcha-error-codes.ts +17 -0
- package/src/localization/email-otp-error-codes.ts +7 -0
- package/src/localization/generic-oauth-error-codes.ts +3 -0
- package/src/localization/haveibeenpwned-error-codes.ts +4 -0
- package/src/localization/multi-session-error-codes.ts +3 -0
- package/src/localization/organization-error-codes.ts +57 -0
- package/src/localization/passkey-error-codes.ts +10 -0
- package/src/localization/phone-number-error-codes.ts +10 -0
- package/src/localization/stripe-localization.ts +12 -0
- package/src/localization/two-factor-error-codes.ts +12 -0
- package/src/localization/username-error-codes.ts +9 -0
- package/src/server.ts +4 -0
- package/src/style.css +1 -0
- package/src/tanstack.ts +1 -0
- package/src/triplit.ts +1 -0
- package/src/types/account-options.ts +35 -0
- package/src/types/additional-fields.ts +21 -0
- package/src/types/any-auth-client.ts +6 -0
- package/src/types/api-key.ts +9 -0
- package/src/types/auth-client.ts +37 -0
- package/src/types/auth-hooks.ts +61 -0
- package/src/types/auth-mutators.ts +17 -0
- package/src/types/avatar-options.ts +29 -0
- package/src/types/captcha-options.ts +32 -0
- package/src/types/captcha-provider.ts +6 -0
- package/src/types/credentials-options.ts +32 -0
- package/src/types/delete-user-options.ts +7 -0
- package/src/types/fetch-error.ts +6 -0
- package/src/types/generic-oauth-options.ts +16 -0
- package/src/types/gravatar-options.ts +21 -0
- package/src/types/image.ts +7 -0
- package/src/types/invitation.ts +10 -0
- package/src/types/link.ts +7 -0
- package/src/types/organization-options.ts +106 -0
- package/src/types/password-validation.ts +16 -0
- package/src/types/profile.ts +15 -0
- package/src/types/refetch.ts +1 -0
- package/src/types/render-toast.ts +9 -0
- package/src/types/sign-up-options.ts +7 -0
- package/src/types/social-options.ts +16 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { createContext, type ReactNode, useMemo } from "react"
|
|
4
|
+
import { toast } from "sonner"
|
|
5
|
+
|
|
6
|
+
import { RecaptchaV3 } from "../components/captcha/recaptcha-v3"
|
|
7
|
+
import { useAuthData } from "../hooks/use-auth-data"
|
|
8
|
+
import {
|
|
9
|
+
type AuthLocalization,
|
|
10
|
+
authLocalization
|
|
11
|
+
} from "../localization/auth-localization"
|
|
12
|
+
import type {
|
|
13
|
+
AccountOptions,
|
|
14
|
+
AccountOptionsContext
|
|
15
|
+
} from "../types/account-options"
|
|
16
|
+
import type { AdditionalFields } from "../types/additional-fields"
|
|
17
|
+
import type { AnyAuthClient } from "../types/any-auth-client"
|
|
18
|
+
import type { AuthClient } from "../types/auth-client"
|
|
19
|
+
import type { AuthHooks } from "../types/auth-hooks"
|
|
20
|
+
import type { AuthMutators } from "../types/auth-mutators"
|
|
21
|
+
import type { AvatarOptions } from "../types/avatar-options"
|
|
22
|
+
import type { CaptchaOptions } from "../types/captcha-options"
|
|
23
|
+
import type { CredentialsOptions } from "../types/credentials-options"
|
|
24
|
+
import type { DeleteUserOptions } from "../types/delete-user-options"
|
|
25
|
+
import type { GenericOAuthOptions } from "../types/generic-oauth-options"
|
|
26
|
+
import type { GravatarOptions } from "../types/gravatar-options"
|
|
27
|
+
import type { Link } from "../types/link"
|
|
28
|
+
import type {
|
|
29
|
+
OrganizationOptions,
|
|
30
|
+
OrganizationOptionsContext
|
|
31
|
+
} from "../types/organization-options"
|
|
32
|
+
import type { RenderToast } from "../types/render-toast"
|
|
33
|
+
import type { SignUpOptions } from "../types/sign-up-options"
|
|
34
|
+
import type { SocialOptions } from "../types/social-options"
|
|
35
|
+
import { OrganizationRefetcher } from "./organization-refetcher"
|
|
36
|
+
import type { AuthViewPaths } from "./view-paths"
|
|
37
|
+
import {
|
|
38
|
+
accountViewPaths,
|
|
39
|
+
authViewPaths,
|
|
40
|
+
organizationViewPaths
|
|
41
|
+
} from "./view-paths"
|
|
42
|
+
|
|
43
|
+
const DefaultLink: Link = ({ href, className, children }) => (
|
|
44
|
+
<a className={className} href={href}>
|
|
45
|
+
{children}
|
|
46
|
+
</a>
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const defaultNavigate = (href: string) => {
|
|
50
|
+
window.location.href = href
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const defaultReplace = (href: string) => {
|
|
54
|
+
window.location.replace(href)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const defaultToast: RenderToast = ({ variant = "default", message }) => {
|
|
58
|
+
if (variant === "default") {
|
|
59
|
+
toast(message)
|
|
60
|
+
} else {
|
|
61
|
+
toast[variant](message)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type AuthUIContextType = {
|
|
66
|
+
authClient: AuthClient
|
|
67
|
+
/**
|
|
68
|
+
* Additional fields for users
|
|
69
|
+
*/
|
|
70
|
+
additionalFields?: AdditionalFields
|
|
71
|
+
/**
|
|
72
|
+
* API Key plugin configuration
|
|
73
|
+
*/
|
|
74
|
+
apiKey?:
|
|
75
|
+
| {
|
|
76
|
+
/**
|
|
77
|
+
* Prefix for API Keys
|
|
78
|
+
*/
|
|
79
|
+
prefix?: string
|
|
80
|
+
/**
|
|
81
|
+
* Metadata for API Keys
|
|
82
|
+
*/
|
|
83
|
+
metadata?: Record<string, unknown>
|
|
84
|
+
}
|
|
85
|
+
| boolean
|
|
86
|
+
/**
|
|
87
|
+
* Avatar configuration
|
|
88
|
+
* @default undefined
|
|
89
|
+
*/
|
|
90
|
+
avatar?: AvatarOptions
|
|
91
|
+
/**
|
|
92
|
+
* Base path for the auth views
|
|
93
|
+
* @default "/auth"
|
|
94
|
+
*/
|
|
95
|
+
basePath: string
|
|
96
|
+
/**
|
|
97
|
+
* Front end base URL for auth API callbacks
|
|
98
|
+
*/
|
|
99
|
+
baseURL?: string
|
|
100
|
+
/**
|
|
101
|
+
* Captcha configuration
|
|
102
|
+
*/
|
|
103
|
+
captcha?: CaptchaOptions
|
|
104
|
+
credentials?: CredentialsOptions
|
|
105
|
+
/**
|
|
106
|
+
* Default redirect URL after authenticating
|
|
107
|
+
* @default "/"
|
|
108
|
+
*/
|
|
109
|
+
redirectTo: string
|
|
110
|
+
/**
|
|
111
|
+
* Enable or disable user change email support
|
|
112
|
+
* @default true
|
|
113
|
+
*/
|
|
114
|
+
changeEmail?: boolean
|
|
115
|
+
/**
|
|
116
|
+
* User Account deletion configuration
|
|
117
|
+
* @default undefined
|
|
118
|
+
*/
|
|
119
|
+
deleteUser?: DeleteUserOptions
|
|
120
|
+
/**
|
|
121
|
+
* Show Verify Email card for unverified emails
|
|
122
|
+
*/
|
|
123
|
+
emailVerification?: boolean
|
|
124
|
+
/**
|
|
125
|
+
* Freshness age for Session data
|
|
126
|
+
* @default 60 * 60 * 24
|
|
127
|
+
*/
|
|
128
|
+
freshAge: number
|
|
129
|
+
/**
|
|
130
|
+
* Generic OAuth provider configuration
|
|
131
|
+
*/
|
|
132
|
+
genericOAuth?: GenericOAuthOptions
|
|
133
|
+
/**
|
|
134
|
+
* Gravatar configuration
|
|
135
|
+
*/
|
|
136
|
+
gravatar?: boolean | GravatarOptions
|
|
137
|
+
hooks: AuthHooks
|
|
138
|
+
localization: typeof authLocalization
|
|
139
|
+
/**
|
|
140
|
+
* Enable or disable Magic Link support
|
|
141
|
+
* @default false
|
|
142
|
+
*/
|
|
143
|
+
magicLink?: boolean
|
|
144
|
+
/**
|
|
145
|
+
* Enable or disable Email OTP support
|
|
146
|
+
* @default false
|
|
147
|
+
*/
|
|
148
|
+
emailOTP?: boolean
|
|
149
|
+
/**
|
|
150
|
+
* Enable or disable Multi Session support
|
|
151
|
+
* @default false
|
|
152
|
+
*/
|
|
153
|
+
multiSession?: boolean
|
|
154
|
+
mutators: AuthMutators
|
|
155
|
+
/**
|
|
156
|
+
* Whether the name field should be required
|
|
157
|
+
* @default true
|
|
158
|
+
*/
|
|
159
|
+
nameRequired?: boolean
|
|
160
|
+
/**
|
|
161
|
+
* Enable or disable One Tap support
|
|
162
|
+
* @default false
|
|
163
|
+
*/
|
|
164
|
+
oneTap?: boolean
|
|
165
|
+
/**
|
|
166
|
+
* Perform some User updates optimistically
|
|
167
|
+
* @default false
|
|
168
|
+
*/
|
|
169
|
+
optimistic?: boolean
|
|
170
|
+
/**
|
|
171
|
+
* Organization configuration
|
|
172
|
+
*/
|
|
173
|
+
organization?: OrganizationOptionsContext
|
|
174
|
+
/**
|
|
175
|
+
* Enable or disable Passkey support
|
|
176
|
+
* @default false
|
|
177
|
+
*/
|
|
178
|
+
passkey?: boolean
|
|
179
|
+
/**
|
|
180
|
+
* Forces better-auth-tanstack to refresh the Session on the auth callback page
|
|
181
|
+
* @default false
|
|
182
|
+
*/
|
|
183
|
+
persistClient?: boolean
|
|
184
|
+
/**
|
|
185
|
+
* Account configuration
|
|
186
|
+
*/
|
|
187
|
+
account?: AccountOptionsContext
|
|
188
|
+
/**
|
|
189
|
+
* Sign Up configuration
|
|
190
|
+
*/
|
|
191
|
+
signUp?: SignUpOptions
|
|
192
|
+
/**
|
|
193
|
+
* Social provider configuration
|
|
194
|
+
*/
|
|
195
|
+
social?: SocialOptions
|
|
196
|
+
toast: RenderToast
|
|
197
|
+
/**
|
|
198
|
+
* Enable or disable two-factor authentication support
|
|
199
|
+
* @default undefined
|
|
200
|
+
*/
|
|
201
|
+
twoFactor?: ("otp" | "totp")[]
|
|
202
|
+
viewPaths: AuthViewPaths
|
|
203
|
+
/**
|
|
204
|
+
* Navigate to a new URL
|
|
205
|
+
* @default window.location.href
|
|
206
|
+
*/
|
|
207
|
+
navigate: (href: string) => void
|
|
208
|
+
/**
|
|
209
|
+
* Called whenever the Session changes
|
|
210
|
+
*/
|
|
211
|
+
onSessionChange?: () => void | Promise<void>
|
|
212
|
+
/**
|
|
213
|
+
* Replace the current URL
|
|
214
|
+
* @default navigate
|
|
215
|
+
*/
|
|
216
|
+
replace: (href: string) => void
|
|
217
|
+
/**
|
|
218
|
+
* Custom Link component for navigation
|
|
219
|
+
* @default <a>
|
|
220
|
+
*/
|
|
221
|
+
Link: Link
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export type AuthUIProviderProps = {
|
|
225
|
+
children: ReactNode
|
|
226
|
+
/**
|
|
227
|
+
* Better Auth client returned from createAuthClient
|
|
228
|
+
* @default Required
|
|
229
|
+
* @remarks `AuthClient`
|
|
230
|
+
*/
|
|
231
|
+
authClient: AnyAuthClient
|
|
232
|
+
/**
|
|
233
|
+
* Enable account view & account configuration
|
|
234
|
+
* @default { fields: ["image", "name"] }
|
|
235
|
+
*/
|
|
236
|
+
account?: boolean | Partial<AccountOptions>
|
|
237
|
+
/**
|
|
238
|
+
* Avatar configuration
|
|
239
|
+
* @default undefined
|
|
240
|
+
*/
|
|
241
|
+
avatar?: boolean | Partial<AvatarOptions>
|
|
242
|
+
/**
|
|
243
|
+
* User Account deletion configuration
|
|
244
|
+
* @default undefined
|
|
245
|
+
*/
|
|
246
|
+
deleteUser?: DeleteUserOptions | boolean
|
|
247
|
+
/**
|
|
248
|
+
* ADVANCED: Custom hooks for fetching auth data
|
|
249
|
+
*/
|
|
250
|
+
hooks?: Partial<AuthHooks>
|
|
251
|
+
/**
|
|
252
|
+
* Customize the paths for the auth views
|
|
253
|
+
* @default authViewPaths
|
|
254
|
+
* @remarks `AuthViewPaths`
|
|
255
|
+
*/
|
|
256
|
+
viewPaths?: Partial<AuthViewPaths>
|
|
257
|
+
/**
|
|
258
|
+
* Render custom Toasts
|
|
259
|
+
* @default Sonner
|
|
260
|
+
*/
|
|
261
|
+
toast?: RenderToast
|
|
262
|
+
/**
|
|
263
|
+
* Customize the Localization strings
|
|
264
|
+
* @default authLocalization
|
|
265
|
+
* @remarks `AuthLocalization`
|
|
266
|
+
*/
|
|
267
|
+
localization?: AuthLocalization
|
|
268
|
+
/**
|
|
269
|
+
* ADVANCED: Custom mutators for updating auth data
|
|
270
|
+
*/
|
|
271
|
+
mutators?: Partial<AuthMutators>
|
|
272
|
+
/**
|
|
273
|
+
* Organization plugin configuration
|
|
274
|
+
*/
|
|
275
|
+
organization?: OrganizationOptions | boolean
|
|
276
|
+
/**
|
|
277
|
+
* Enable or disable Credentials support
|
|
278
|
+
* @default { forgotPassword: true }
|
|
279
|
+
*/
|
|
280
|
+
credentials?: boolean | CredentialsOptions
|
|
281
|
+
/**
|
|
282
|
+
* Enable or disable Sign Up form
|
|
283
|
+
* @default { fields: ["name"] }
|
|
284
|
+
*/
|
|
285
|
+
signUp?: SignUpOptions | boolean
|
|
286
|
+
} & Partial<
|
|
287
|
+
Omit<
|
|
288
|
+
AuthUIContextType,
|
|
289
|
+
| "authClient"
|
|
290
|
+
| "viewPaths"
|
|
291
|
+
| "localization"
|
|
292
|
+
| "mutators"
|
|
293
|
+
| "toast"
|
|
294
|
+
| "hooks"
|
|
295
|
+
| "avatar"
|
|
296
|
+
| "account"
|
|
297
|
+
| "deleteUser"
|
|
298
|
+
| "credentials"
|
|
299
|
+
| "signUp"
|
|
300
|
+
| "organization"
|
|
301
|
+
>
|
|
302
|
+
>
|
|
303
|
+
|
|
304
|
+
export const AuthUIContext = createContext<AuthUIContextType>(
|
|
305
|
+
{} as unknown as AuthUIContextType
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
export const AuthUIProvider = ({
|
|
309
|
+
children,
|
|
310
|
+
authClient: authClientProp,
|
|
311
|
+
account: accountProp,
|
|
312
|
+
avatar: avatarProp,
|
|
313
|
+
deleteUser: deleteUserProp,
|
|
314
|
+
social: socialProp,
|
|
315
|
+
genericOAuth: genericOAuthProp,
|
|
316
|
+
basePath = "/auth",
|
|
317
|
+
baseURL = "",
|
|
318
|
+
captcha,
|
|
319
|
+
redirectTo = "/",
|
|
320
|
+
credentials: credentialsProp,
|
|
321
|
+
changeEmail = true,
|
|
322
|
+
freshAge = 60 * 60 * 24,
|
|
323
|
+
hooks: hooksProp,
|
|
324
|
+
mutators: mutatorsProp,
|
|
325
|
+
localization: localizationProp,
|
|
326
|
+
nameRequired = true,
|
|
327
|
+
organization: organizationProp,
|
|
328
|
+
signUp: signUpProp = true,
|
|
329
|
+
toast = defaultToast,
|
|
330
|
+
viewPaths: viewPathsProp,
|
|
331
|
+
navigate,
|
|
332
|
+
replace,
|
|
333
|
+
Link = DefaultLink,
|
|
334
|
+
...props
|
|
335
|
+
}: AuthUIProviderProps) => {
|
|
336
|
+
const authClient = authClientProp as AuthClient
|
|
337
|
+
|
|
338
|
+
const avatar = useMemo<AvatarOptions | undefined>(() => {
|
|
339
|
+
if (!avatarProp) return
|
|
340
|
+
|
|
341
|
+
if (avatarProp === true) {
|
|
342
|
+
return {
|
|
343
|
+
extension: "png",
|
|
344
|
+
size: 128
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
upload: avatarProp.upload,
|
|
350
|
+
delete: avatarProp.delete,
|
|
351
|
+
extension: avatarProp.extension || "png",
|
|
352
|
+
size: avatarProp.size || (avatarProp.upload ? 256 : 128)
|
|
353
|
+
}
|
|
354
|
+
}, [avatarProp])
|
|
355
|
+
|
|
356
|
+
const account = useMemo<AccountOptionsContext | undefined>(() => {
|
|
357
|
+
if (accountProp === false) return
|
|
358
|
+
|
|
359
|
+
if (accountProp === true || accountProp === undefined) {
|
|
360
|
+
return {
|
|
361
|
+
basePath: "/account",
|
|
362
|
+
fields: ["image", "name"],
|
|
363
|
+
viewPaths: accountViewPaths
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Remove trailing slash from basePath
|
|
368
|
+
const basePath = accountProp.basePath?.endsWith("/")
|
|
369
|
+
? accountProp.basePath.slice(0, -1)
|
|
370
|
+
: accountProp.basePath
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
basePath: basePath ?? "/account",
|
|
374
|
+
fields: accountProp.fields || ["image", "name"],
|
|
375
|
+
viewPaths: { ...accountViewPaths, ...accountProp.viewPaths }
|
|
376
|
+
}
|
|
377
|
+
}, [accountProp])
|
|
378
|
+
|
|
379
|
+
const deleteUser = useMemo<DeleteUserOptions | undefined>(() => {
|
|
380
|
+
if (!deleteUserProp) return
|
|
381
|
+
|
|
382
|
+
if (deleteUserProp === true) {
|
|
383
|
+
return {}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return deleteUserProp
|
|
387
|
+
}, [deleteUserProp])
|
|
388
|
+
|
|
389
|
+
const social = useMemo<SocialOptions | undefined>(() => {
|
|
390
|
+
if (!socialProp) return
|
|
391
|
+
|
|
392
|
+
return socialProp
|
|
393
|
+
}, [socialProp])
|
|
394
|
+
|
|
395
|
+
const genericOAuth = useMemo<GenericOAuthOptions | undefined>(() => {
|
|
396
|
+
if (!genericOAuthProp) return
|
|
397
|
+
|
|
398
|
+
return genericOAuthProp
|
|
399
|
+
}, [genericOAuthProp])
|
|
400
|
+
|
|
401
|
+
const credentials = useMemo<CredentialsOptions | undefined>(() => {
|
|
402
|
+
if (credentialsProp === false) return
|
|
403
|
+
|
|
404
|
+
if (credentialsProp === true) {
|
|
405
|
+
return {
|
|
406
|
+
forgotPassword: true
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
...credentialsProp,
|
|
412
|
+
forgotPassword: credentialsProp?.forgotPassword ?? true
|
|
413
|
+
}
|
|
414
|
+
}, [credentialsProp])
|
|
415
|
+
|
|
416
|
+
const signUp = useMemo<SignUpOptions | undefined>(() => {
|
|
417
|
+
if (signUpProp === false) return
|
|
418
|
+
|
|
419
|
+
if (signUpProp === true || signUpProp === undefined) {
|
|
420
|
+
return {
|
|
421
|
+
fields: ["name"]
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return {
|
|
426
|
+
fields: signUpProp.fields || ["name"]
|
|
427
|
+
}
|
|
428
|
+
}, [signUpProp])
|
|
429
|
+
|
|
430
|
+
const organization = useMemo<OrganizationOptionsContext | undefined>(() => {
|
|
431
|
+
if (!organizationProp) return
|
|
432
|
+
|
|
433
|
+
if (organizationProp === true) {
|
|
434
|
+
return {
|
|
435
|
+
basePath: "/organization",
|
|
436
|
+
viewPaths: organizationViewPaths,
|
|
437
|
+
customRoles: []
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
let logo: OrganizationOptionsContext["logo"] | undefined
|
|
442
|
+
|
|
443
|
+
if (organizationProp.logo === true) {
|
|
444
|
+
logo = {
|
|
445
|
+
extension: "png",
|
|
446
|
+
size: 128
|
|
447
|
+
}
|
|
448
|
+
} else if (organizationProp.logo) {
|
|
449
|
+
logo = {
|
|
450
|
+
upload: organizationProp.logo.upload,
|
|
451
|
+
delete: organizationProp.logo.delete,
|
|
452
|
+
extension: organizationProp.logo.extension || "png",
|
|
453
|
+
size:
|
|
454
|
+
organizationProp.logo.size || organizationProp.logo.upload
|
|
455
|
+
? 256
|
|
456
|
+
: 128
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Remove trailing slash from basePath
|
|
461
|
+
const basePath = organizationProp.basePath?.endsWith("/")
|
|
462
|
+
? organizationProp.basePath.slice(0, -1)
|
|
463
|
+
: organizationProp.basePath
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
...organizationProp,
|
|
467
|
+
logo,
|
|
468
|
+
basePath: basePath ?? "/organization",
|
|
469
|
+
customRoles: organizationProp.customRoles || [],
|
|
470
|
+
viewPaths: {
|
|
471
|
+
...organizationViewPaths,
|
|
472
|
+
...organizationProp.viewPaths
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}, [organizationProp])
|
|
476
|
+
|
|
477
|
+
const defaultMutators = useMemo(() => {
|
|
478
|
+
return {
|
|
479
|
+
deleteApiKey: (params) =>
|
|
480
|
+
authClient.apiKey.delete({
|
|
481
|
+
...params,
|
|
482
|
+
fetchOptions: { throw: true }
|
|
483
|
+
}),
|
|
484
|
+
deletePasskey: (params) =>
|
|
485
|
+
authClient.passkey.deletePasskey({
|
|
486
|
+
...params,
|
|
487
|
+
fetchOptions: { throw: true }
|
|
488
|
+
}),
|
|
489
|
+
revokeDeviceSession: (params) =>
|
|
490
|
+
authClient.multiSession.revoke({
|
|
491
|
+
...params,
|
|
492
|
+
fetchOptions: { throw: true }
|
|
493
|
+
}),
|
|
494
|
+
revokeSession: (params) =>
|
|
495
|
+
authClient.revokeSession({
|
|
496
|
+
...params,
|
|
497
|
+
fetchOptions: { throw: true }
|
|
498
|
+
}),
|
|
499
|
+
setActiveSession: (params) =>
|
|
500
|
+
authClient.multiSession.setActive({
|
|
501
|
+
...params,
|
|
502
|
+
fetchOptions: { throw: true }
|
|
503
|
+
}),
|
|
504
|
+
updateOrganization: (params) =>
|
|
505
|
+
authClient.organization.update({
|
|
506
|
+
...params,
|
|
507
|
+
fetchOptions: { throw: true }
|
|
508
|
+
}),
|
|
509
|
+
updateUser: (params) =>
|
|
510
|
+
authClient.updateUser({
|
|
511
|
+
...params,
|
|
512
|
+
fetchOptions: { throw: true }
|
|
513
|
+
}),
|
|
514
|
+
unlinkAccount: (params) =>
|
|
515
|
+
authClient.unlinkAccount({
|
|
516
|
+
...params,
|
|
517
|
+
fetchOptions: { throw: true }
|
|
518
|
+
})
|
|
519
|
+
} as AuthMutators
|
|
520
|
+
}, [authClient])
|
|
521
|
+
|
|
522
|
+
const defaultHooks = useMemo(() => {
|
|
523
|
+
return {
|
|
524
|
+
useSession: authClient.useSession,
|
|
525
|
+
useListAccounts: () =>
|
|
526
|
+
useAuthData({
|
|
527
|
+
queryFn: authClient.listAccounts,
|
|
528
|
+
cacheKey: "listAccounts"
|
|
529
|
+
}),
|
|
530
|
+
useAccountInfo: (params) =>
|
|
531
|
+
useAuthData({
|
|
532
|
+
queryFn: () => authClient.accountInfo(params),
|
|
533
|
+
cacheKey: `accountInfo:${JSON.stringify(params)}`
|
|
534
|
+
}),
|
|
535
|
+
useListDeviceSessions: () =>
|
|
536
|
+
useAuthData({
|
|
537
|
+
queryFn: authClient.multiSession.listDeviceSessions,
|
|
538
|
+
cacheKey: "listDeviceSessions"
|
|
539
|
+
}),
|
|
540
|
+
useListSessions: () =>
|
|
541
|
+
useAuthData({
|
|
542
|
+
queryFn: authClient.listSessions,
|
|
543
|
+
cacheKey: "listSessions"
|
|
544
|
+
}),
|
|
545
|
+
useListPasskeys: authClient.useListPasskeys,
|
|
546
|
+
useListApiKeys: () =>
|
|
547
|
+
useAuthData({
|
|
548
|
+
queryFn: authClient.apiKey.list,
|
|
549
|
+
cacheKey: "listApiKeys"
|
|
550
|
+
}),
|
|
551
|
+
useActiveOrganization: authClient.useActiveOrganization,
|
|
552
|
+
useListOrganizations: authClient.useListOrganizations,
|
|
553
|
+
useHasPermission: (params) =>
|
|
554
|
+
useAuthData({
|
|
555
|
+
queryFn: () =>
|
|
556
|
+
authClient.$fetch("/organization/has-permission", {
|
|
557
|
+
method: "POST",
|
|
558
|
+
body: params
|
|
559
|
+
}),
|
|
560
|
+
cacheKey: `hasPermission:${JSON.stringify(params)}`
|
|
561
|
+
}),
|
|
562
|
+
useInvitation: (params) =>
|
|
563
|
+
useAuthData({
|
|
564
|
+
queryFn: () =>
|
|
565
|
+
authClient.organization.getInvitation(params),
|
|
566
|
+
cacheKey: `invitation:${JSON.stringify(params)}`
|
|
567
|
+
}),
|
|
568
|
+
useListInvitations: (params) =>
|
|
569
|
+
useAuthData({
|
|
570
|
+
queryFn: () =>
|
|
571
|
+
authClient.$fetch(
|
|
572
|
+
`/organization/list-invitations?organizationId=${params?.query?.organizationId || ""}`
|
|
573
|
+
),
|
|
574
|
+
cacheKey: `listInvitations:${JSON.stringify(params)}`
|
|
575
|
+
}),
|
|
576
|
+
useListUserInvitations: () =>
|
|
577
|
+
useAuthData({
|
|
578
|
+
queryFn: () =>
|
|
579
|
+
authClient.$fetch(
|
|
580
|
+
"/organization/list-user-invitations"
|
|
581
|
+
),
|
|
582
|
+
cacheKey: `listUserInvitations`
|
|
583
|
+
}),
|
|
584
|
+
useListMembers: (params) =>
|
|
585
|
+
useAuthData({
|
|
586
|
+
queryFn: () =>
|
|
587
|
+
authClient.$fetch(
|
|
588
|
+
`/organization/list-members?organizationId=${params?.query?.organizationId || ""}`
|
|
589
|
+
),
|
|
590
|
+
cacheKey: `listMembers:${JSON.stringify(params)}`
|
|
591
|
+
})
|
|
592
|
+
} as AuthHooks
|
|
593
|
+
}, [authClient])
|
|
594
|
+
|
|
595
|
+
const viewPaths = useMemo(() => {
|
|
596
|
+
return { ...authViewPaths, ...viewPathsProp }
|
|
597
|
+
}, [viewPathsProp])
|
|
598
|
+
|
|
599
|
+
const localization = useMemo(() => {
|
|
600
|
+
return { ...authLocalization, ...localizationProp }
|
|
601
|
+
}, [localizationProp])
|
|
602
|
+
|
|
603
|
+
const hooks = useMemo(() => {
|
|
604
|
+
return { ...defaultHooks, ...hooksProp }
|
|
605
|
+
}, [defaultHooks, hooksProp])
|
|
606
|
+
|
|
607
|
+
const mutators = useMemo(() => {
|
|
608
|
+
return { ...defaultMutators, ...mutatorsProp }
|
|
609
|
+
}, [defaultMutators, mutatorsProp])
|
|
610
|
+
|
|
611
|
+
// Remove trailing slash from baseURL
|
|
612
|
+
baseURL = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL
|
|
613
|
+
|
|
614
|
+
// Remove trailing slash from basePath
|
|
615
|
+
basePath = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath
|
|
616
|
+
|
|
617
|
+
const { data: sessionData } = hooks.useSession()
|
|
618
|
+
|
|
619
|
+
return (
|
|
620
|
+
<AuthUIContext.Provider
|
|
621
|
+
value={{
|
|
622
|
+
authClient,
|
|
623
|
+
avatar,
|
|
624
|
+
basePath: basePath === "/" ? "" : basePath,
|
|
625
|
+
baseURL,
|
|
626
|
+
captcha,
|
|
627
|
+
redirectTo,
|
|
628
|
+
changeEmail,
|
|
629
|
+
credentials,
|
|
630
|
+
deleteUser,
|
|
631
|
+
freshAge,
|
|
632
|
+
genericOAuth,
|
|
633
|
+
hooks,
|
|
634
|
+
mutators,
|
|
635
|
+
localization,
|
|
636
|
+
nameRequired,
|
|
637
|
+
organization,
|
|
638
|
+
account,
|
|
639
|
+
signUp,
|
|
640
|
+
social,
|
|
641
|
+
toast,
|
|
642
|
+
navigate: navigate || defaultNavigate,
|
|
643
|
+
replace: replace || navigate || defaultReplace,
|
|
644
|
+
viewPaths,
|
|
645
|
+
Link,
|
|
646
|
+
...props
|
|
647
|
+
}}
|
|
648
|
+
>
|
|
649
|
+
{sessionData && organization && <OrganizationRefetcher />}
|
|
650
|
+
|
|
651
|
+
{captcha?.provider === "google-recaptcha-v3" ? (
|
|
652
|
+
<RecaptchaV3>{children}</RecaptchaV3>
|
|
653
|
+
) : (
|
|
654
|
+
children
|
|
655
|
+
)}
|
|
656
|
+
</AuthUIContext.Provider>
|
|
657
|
+
)
|
|
658
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { sha256 } from "@noble/hashes/sha2.js"
|
|
2
|
+
import { bytesToHex } from "@noble/hashes/utils.js"
|
|
3
|
+
import type { GravatarOptions } from "../types/gravatar-options"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate a Gravatar URL for an email address
|
|
7
|
+
* @param email - Email address
|
|
8
|
+
* @param options - Gravatar options
|
|
9
|
+
* @returns Gravatar URL or null if email is invalid
|
|
10
|
+
*/
|
|
11
|
+
export function getGravatarUrl(
|
|
12
|
+
email?: string | null,
|
|
13
|
+
options?: GravatarOptions
|
|
14
|
+
): string | null {
|
|
15
|
+
if (!email) return null
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Normalize email: trim and lowercase
|
|
19
|
+
const normalizedEmail = email.trim().toLowerCase()
|
|
20
|
+
// sha256 expects Uint8Array, so encode string to Uint8Array
|
|
21
|
+
const encoder = new TextEncoder()
|
|
22
|
+
const emailBytes = encoder.encode(normalizedEmail)
|
|
23
|
+
const hash = bytesToHex(sha256(emailBytes))
|
|
24
|
+
const extension = options?.jpg ? ".jpg" : ""
|
|
25
|
+
let url = `https://gravatar.com/avatar/${hash}${extension}`
|
|
26
|
+
|
|
27
|
+
const params = new URLSearchParams()
|
|
28
|
+
|
|
29
|
+
// Add size parameter
|
|
30
|
+
if (options?.size) {
|
|
31
|
+
params.append(
|
|
32
|
+
"s",
|
|
33
|
+
Math.min(Math.max(options.size, 1), 2048).toString()
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Add default image parameter
|
|
38
|
+
if (options?.d) {
|
|
39
|
+
params.append("d", options.d)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Add force default parameter
|
|
43
|
+
if (options?.forceDefault) {
|
|
44
|
+
params.append("f", "y")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Append parameters if any
|
|
48
|
+
const queryString = params.toString()
|
|
49
|
+
if (queryString) {
|
|
50
|
+
url += `?${queryString}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return url
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error("Error generating Gravatar URL:", error)
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
}
|