better-auth-ui-svelte 0.2.3 → 0.3.6
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/README.md +25 -4
- package/dist/auth-client.d.ts +8 -0
- package/dist/auth-client.js +3 -2
- package/dist/components/account/account-view.svelte +8 -8
- package/dist/components/account/account-view.svelte.d.ts +1 -3
- package/dist/components/auth/auth-form.svelte +1 -4
- package/dist/components/auth/auth-view.svelte +8 -2
- package/dist/components/auth/forms/email-otp-form.svelte +0 -2
- package/dist/components/auth/forms/email-otp-form.svelte.d.ts +0 -1
- package/dist/components/auth/forms/forgot-password-form.svelte +2 -2
- package/dist/components/auth/forms/magic-link-form.svelte +17 -4
- package/dist/components/auth/forms/sign-in-form.svelte +2 -2
- package/dist/components/auth/forms/sign-up-form.svelte +50 -25
- package/dist/components/auth/forms/two-factor-form.svelte +1 -5
- package/dist/components/auth/magic-link-button.svelte +1 -1
- package/dist/components/auth/magic-link-sent.svelte +204 -0
- package/dist/components/auth/magic-link-sent.svelte.d.ts +18 -0
- package/dist/components/auth/verify-email.svelte +319 -0
- package/dist/components/auth/verify-email.svelte.d.ts +18 -0
- package/dist/components/auth-ui-provider.svelte +55 -8
- package/dist/components/auth-ui-provider.svelte.d.ts +29 -5
- package/dist/components/captcha/captcha.svelte +1 -3
- package/dist/components/captcha/captcha.svelte.d.ts +1 -1
- package/dist/components/captcha/recaptcha-v2.svelte +2 -7
- package/dist/components/captcha/recaptcha-v2.svelte.d.ts +1 -1
- package/dist/components/captcha/recaptcha-v3.svelte +5 -6
- package/dist/components/default-link.svelte +2 -1
- package/dist/components/organization/accept-invitation-card.svelte +1 -1
- package/dist/components/organization/accept-invitation-card.svelte.d.ts +1 -2
- package/dist/components/organization/create-organization-dialog.svelte +0 -2
- package/dist/components/organization/create-organization-dialog.svelte.d.ts +0 -1
- package/dist/components/organization/delete-organization-card.svelte +1 -1
- package/dist/components/organization/delete-organization-card.svelte.d.ts +1 -3
- package/dist/components/organization/delete-organization-dialog.svelte +2 -2
- package/dist/components/organization/delete-organization-form.svelte +1 -1
- package/dist/components/organization/invitation-cell.svelte +18 -14
- package/dist/components/organization/invite-member-dialog.svelte +1 -7
- package/dist/components/organization/invite-member-dialog.svelte.d.ts +0 -1
- package/dist/components/organization/leave-organization-dialog.svelte +1 -1
- package/dist/components/organization/member-cell.svelte +4 -2
- package/dist/components/organization/organization-invitations-card.svelte +1 -1
- package/dist/components/organization/organization-invitations-card.svelte.d.ts +1 -3
- package/dist/components/organization/organization-logo-card.svelte +1 -1
- package/dist/components/organization/organization-logo-card.svelte.d.ts +1 -3
- package/dist/components/organization/organization-logo-form.svelte +2 -2
- package/dist/components/organization/organization-logo-form.svelte.d.ts +1 -3
- package/dist/components/organization/organization-members-card.svelte +3 -3
- package/dist/components/organization/organization-members-card.svelte.d.ts +1 -3
- package/dist/components/organization/organization-name-card.svelte +1 -1
- package/dist/components/organization/organization-name-card.svelte.d.ts +1 -3
- package/dist/components/organization/organization-name-form.svelte +2 -2
- package/dist/components/organization/organization-name-form.svelte.d.ts +1 -3
- package/dist/components/organization/organization-settings-cards.svelte +1 -1
- package/dist/components/organization/organization-settings-cards.svelte.d.ts +1 -3
- package/dist/components/organization/organization-slug-card.svelte +1 -1
- package/dist/components/organization/organization-slug-card.svelte.d.ts +1 -3
- package/dist/components/organization/organization-slug-form.svelte +2 -2
- package/dist/components/organization/organization-slug-form.svelte.d.ts +1 -3
- package/dist/components/organization/organization-switcher.svelte +1 -1
- package/dist/components/organization/organization-view.svelte +6 -4
- package/dist/components/organization/organization-view.svelte.d.ts +1 -3
- package/dist/components/organization/organizations-card.svelte +1 -1
- package/dist/components/organization/organizations-card.svelte.d.ts +1 -3
- package/dist/components/organization/remove-member-dialog.svelte +0 -2
- package/dist/components/organization/remove-member-dialog.svelte.d.ts +0 -1
- package/dist/components/organization/update-member-role-dialog.svelte +1 -3
- package/dist/components/organization/user-invitations-card.svelte +1 -1
- package/dist/components/organization/user-invitations-card.svelte.d.ts +1 -3
- package/dist/components/organization-refetcher.svelte +8 -3
- package/dist/components/settings/account/account-cell.svelte +18 -18
- package/dist/components/settings/account/account-settings-cards.svelte +1 -3
- package/dist/components/settings/account/account-settings-cards.svelte.d.ts +1 -3
- package/dist/components/settings/account/accounts-card.svelte +1 -1
- package/dist/components/settings/account/accounts-card.svelte.d.ts +1 -3
- package/dist/components/settings/account/delete-account-card.svelte +1 -1
- package/dist/components/settings/account/delete-account-card.svelte.d.ts +1 -3
- package/dist/components/settings/account/delete-account-dialog.svelte +0 -9
- package/dist/components/settings/account/update-avatar-card.svelte +1 -1
- package/dist/components/settings/account/update-avatar-card.svelte.d.ts +1 -3
- package/dist/components/settings/account/update-field-card.svelte +4 -4
- package/dist/components/settings/account/update-field-card.svelte.d.ts +1 -3
- package/dist/components/settings/account/update-name-card.svelte +1 -1
- package/dist/components/settings/account/update-name-card.svelte.d.ts +1 -3
- package/dist/components/settings/account/update-username-card.svelte +1 -1
- package/dist/components/settings/account/update-username-card.svelte.d.ts +1 -3
- package/dist/components/settings/api-key/api-key-cell.svelte +11 -20
- package/dist/components/settings/api-key/api-key-cell.svelte.d.ts +1 -3
- package/dist/components/settings/api-key/api-key-delete-dialog.svelte +3 -2
- package/dist/components/settings/api-key/api-key-display-dialog.svelte +1 -1
- package/dist/components/settings/api-key/api-keys-card.svelte +7 -10
- package/dist/components/settings/api-key/api-keys-card.svelte.d.ts +1 -3
- package/dist/components/settings/api-key/create-api-key-dialog.svelte +32 -33
- package/dist/components/settings/api-key/create-api-key-dialog.svelte.d.ts +0 -1
- package/dist/components/settings/passkey/passkey-cell.svelte +2 -8
- package/dist/components/settings/passkey/passkey-cell.svelte.d.ts +1 -3
- package/dist/components/settings/passkey/passkeys-card.svelte +11 -15
- package/dist/components/settings/passkey/passkeys-card.svelte.d.ts +1 -3
- package/dist/components/settings/providers/provider-cell.svelte +12 -3
- package/dist/components/settings/providers/providers-card.svelte +3 -10
- package/dist/components/settings/providers/providers-card.svelte.d.ts +1 -3
- package/dist/components/settings/security/change-email-card.svelte +13 -6
- package/dist/components/settings/security/change-email-card.svelte.d.ts +1 -3
- package/dist/components/settings/security/change-password-card.svelte +2 -4
- package/dist/components/settings/security/change-password-card.svelte.d.ts +1 -3
- package/dist/components/settings/security/session-cell.svelte +3 -3
- package/dist/components/settings/security/session-cell.svelte.d.ts +1 -3
- package/dist/components/settings/security/sessions-card.svelte +3 -10
- package/dist/components/settings/security/sessions-card.svelte.d.ts +1 -3
- package/dist/components/settings/security-settings-cards.svelte +4 -12
- package/dist/components/settings/security-settings-cards.svelte.d.ts +1 -3
- package/dist/components/settings/shared/session-freshness-dialog.svelte +1 -1
- package/dist/components/settings/shared/session-freshness-dialog.svelte.d.ts +1 -3
- package/dist/components/settings/shared/settings-card-footer.svelte +0 -1
- package/dist/components/settings/shared/settings-card-footer.svelte.d.ts +0 -1
- package/dist/components/settings/shared/settings-card.svelte +0 -5
- package/dist/components/settings/shared/settings-card.svelte.d.ts +0 -3
- package/dist/components/settings/two-factor/backup-codes-dialog.svelte +1 -4
- package/dist/components/settings/two-factor/two-factor-card.svelte +10 -4
- package/dist/components/settings/two-factor/two-factor-card.svelte.d.ts +1 -3
- package/dist/components/ui/button/button.svelte +2 -0
- package/dist/components/ui/dropdown-menu/index.d.ts +19 -0
- package/dist/components/ui/input-otp/input-otp.svelte +0 -1
- package/dist/components/ui/qr-code/qr-code.js +9 -9
- package/dist/components/ui/qr-code/qr-code.svelte +1 -0
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/components/user-view.svelte +1 -1
- package/dist/config/auth-config.js +3 -2
- package/dist/context/auth-ui-config.svelte.d.ts +21 -0
- package/dist/hooks/use-authenticate.svelte.d.ts +1 -1
- package/dist/hooks/use-captcha.svelte.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/localization/auth-localization.d.ts +42 -0
- package/dist/localization/auth-localization.js +46 -2
- package/dist/localization/stripe-localization.js +1 -1
- package/dist/server/auth.d.ts +2653 -10870
- package/dist/server/auth.js +39 -4
- package/dist/server/db/index.d.ts +1 -1
- package/dist/server/db/schema.d.ts +115 -115
- package/dist/social-providers.d.ts +7 -0
- package/dist/types/any-auth-client.d.ts +1 -1
- package/dist/types/index.d.ts +24 -2
- package/dist/utils/view-paths.d.ts +4 -0
- package/dist/utils/view-paths.js +5 -1
- package/package.json +17 -8
package/README.md
CHANGED
|
@@ -14,6 +14,12 @@ This is a **complete Svelte 5 port** of the [Better Auth UI React library](https
|
|
|
14
14
|
|
|
15
15
|
> **Important:** This library is in early stage development. While we have achieved full feature parity with the React version and all components have been ported, the library has not been battle-tested in production environments yet. Issues may arise. **Use at your own risk until we reach v1.0 stable.**
|
|
16
16
|
|
|
17
|
+
## About This Port
|
|
18
|
+
|
|
19
|
+
This Svelte port was primarily built to support my own projects including [stacksee.com](https://stacksee.com) and [textatlas.com](https://textatlas.com). While it currently maintains full feature parity with the original React library, **this port may evolve independently over time**. I may add new features, make different architectural decisions, or implement functionality specifically tailored to my project needs that diverge from the original library.
|
|
20
|
+
|
|
21
|
+
If you're looking for a library that strictly mirrors the React version, please be aware that this port's API and features may change to better serve the needs of my projects and the Svelte ecosystem.
|
|
22
|
+
|
|
17
23
|
## Why Choose Better Auth UI?
|
|
18
24
|
|
|
19
25
|
- **Easy** – Plug & play authentication components
|
|
@@ -34,12 +40,16 @@ This is a **complete Svelte 5 port** of the [Better Auth UI React library](https
|
|
|
34
40
|
|
|
35
41
|
## Installation
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
Ensure you have Better Auth and shadcn set up in your project first.
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
Them install the package:
|
|
40
46
|
|
|
41
47
|
```bash
|
|
42
|
-
pnpm
|
|
48
|
+
pnpm add better-auth-ui-svelte
|
|
49
|
+
|
|
50
|
+
## bun add better-auth-ui-svelte
|
|
51
|
+
## npm install better-auth-ui-svelte
|
|
52
|
+
## yarn add better-auth-ui-svelte
|
|
43
53
|
```
|
|
44
54
|
|
|
45
55
|
### Peer Dependencies
|
|
@@ -345,7 +355,18 @@ interface AuthUIProviderProps {
|
|
|
345
355
|
};
|
|
346
356
|
|
|
347
357
|
// Additional auth methods
|
|
348
|
-
magicLink?:
|
|
358
|
+
magicLink?:
|
|
359
|
+
| boolean
|
|
360
|
+
| {
|
|
361
|
+
resendCooldown?: number;
|
|
362
|
+
redirectToSentPage?: boolean;
|
|
363
|
+
};
|
|
364
|
+
emailVerification?:
|
|
365
|
+
| boolean
|
|
366
|
+
| {
|
|
367
|
+
resendCooldown?: number;
|
|
368
|
+
redirectToVerifyPage?: boolean;
|
|
369
|
+
};
|
|
349
370
|
passkey?: boolean;
|
|
350
371
|
|
|
351
372
|
// Two-factor authentication
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createAuthClient, type InferSessionFromClient } from 'better-auth/svelte';
|
|
2
|
+
import type { Atom } from 'nanostores';
|
|
3
|
+
/**
|
|
4
|
+
* Better Auth Svelte client
|
|
5
|
+
* This provides reactive stores for authentication state
|
|
6
|
+
*/
|
|
7
|
+
export declare const authClient: ReturnType<typeof createAuthClient>;
|
|
8
|
+
export declare const useSession: () => Atom<InferSessionFromClient<typeof authClient>>;
|
package/dist/auth-client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createAuthClient } from 'better-auth/svelte';
|
|
2
|
-
import { organizationClient, apiKeyClient, twoFactorClient, usernameClient, magicLinkClient, emailOTPClient, lastLoginMethodClient, oneTapClient, genericOAuthClient, anonymousClient, multiSessionClient
|
|
2
|
+
import { organizationClient, apiKeyClient, twoFactorClient, usernameClient, magicLinkClient, emailOTPClient, lastLoginMethodClient, oneTapClient, genericOAuthClient, anonymousClient, multiSessionClient } from 'better-auth/client/plugins';
|
|
3
|
+
import { passkeyClient } from '@better-auth/passkey/client';
|
|
3
4
|
/**
|
|
4
5
|
* Better Auth Svelte client
|
|
5
6
|
* This provides reactive stores for authentication state
|
|
@@ -23,4 +24,4 @@ export const authClient = createAuthClient({
|
|
|
23
24
|
]
|
|
24
25
|
});
|
|
25
26
|
// Export convenience methods
|
|
26
|
-
export const
|
|
27
|
+
export const useSession = authClient.useSession;
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
import { Label } from '../ui/label/index.js';
|
|
35
35
|
import { Menu } from '@lucide/svelte';
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
type Props = AccountViewProps;
|
|
38
38
|
|
|
39
39
|
let {
|
|
40
40
|
className,
|
|
@@ -101,9 +101,7 @@
|
|
|
101
101
|
{#if !accountOptions}
|
|
102
102
|
<!-- Return nothing if account options not configured -->
|
|
103
103
|
{:else}
|
|
104
|
-
<div
|
|
105
|
-
class={cn('flex w-full grow flex-col gap-4 md:flex-row md:gap-12', className, classNames?.base)}
|
|
106
|
-
>
|
|
104
|
+
<div class={cn('flex w-full flex-col gap-4 md:flex-row md:gap-12', className, classNames?.base)}>
|
|
107
105
|
{#if !hideNav}
|
|
108
106
|
<!-- Mobile Navigation (Drawer) -->
|
|
109
107
|
<div class="flex justify-between gap-2 md:hidden">
|
|
@@ -113,9 +111,11 @@
|
|
|
113
111
|
|
|
114
112
|
<Drawer.Root bind:open={drawerOpen}>
|
|
115
113
|
<Drawer.Trigger>
|
|
116
|
-
|
|
117
|
-
<
|
|
118
|
-
|
|
114
|
+
{#snippet child({ props })}
|
|
115
|
+
<Button {...props} variant="outline">
|
|
116
|
+
<Menu />
|
|
117
|
+
</Button>
|
|
118
|
+
{/snippet}
|
|
119
119
|
</Drawer.Trigger>
|
|
120
120
|
<Drawer.Content>
|
|
121
121
|
<Drawer.Header>
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
{:else if view === 'SECURITY'}
|
|
181
181
|
<SecuritySettingsCards {classNames} {localization} />
|
|
182
182
|
{:else if view === 'API_KEYS'}
|
|
183
|
-
<ApiKeysCard classNames={classNames?.card} {localization} />
|
|
183
|
+
<ApiKeysCard classNames={classNames?.card} {localization} />
|
|
184
184
|
{:else if view === 'ORGANIZATIONS' && organization}
|
|
185
185
|
<div class="grid w-full gap-4 md:gap-6">
|
|
186
186
|
<OrganizationsCard classNames={classNames?.card} {localization} />
|
|
@@ -22,8 +22,6 @@ export interface AccountViewProps {
|
|
|
22
22
|
view?: AccountViewPath;
|
|
23
23
|
hideNav?: boolean;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
declare const AccountView: import("svelte").Component<Props, {}, "">;
|
|
25
|
+
declare const AccountView: import("svelte").Component<AccountViewProps, {}, "">;
|
|
28
26
|
type AccountView = ReturnType<typeof AccountView>;
|
|
29
27
|
export default AccountView;
|
|
@@ -64,15 +64,12 @@
|
|
|
64
64
|
const config = getAuthUIConfig();
|
|
65
65
|
|
|
66
66
|
const {
|
|
67
|
-
basePath,
|
|
68
67
|
credentials,
|
|
69
68
|
localization: contextLocalization,
|
|
70
69
|
magicLink,
|
|
71
70
|
emailOTP,
|
|
72
71
|
signUp,
|
|
73
|
-
|
|
74
|
-
viewPaths,
|
|
75
|
-
replace
|
|
72
|
+
viewPaths
|
|
76
73
|
} = config;
|
|
77
74
|
|
|
78
75
|
const signUpEnabled = !!signUp;
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
import OneTap from './one-tap.svelte';
|
|
18
18
|
import AuthCallback from './auth-callback.svelte';
|
|
19
19
|
import SignOut from './sign-out.svelte';
|
|
20
|
+
import VerifyEmail from './verify-email.svelte';
|
|
21
|
+
import MagicLinkSent from './magic-link-sent.svelte';
|
|
20
22
|
import type { AuthViewPath } from '../../utils/view-paths.js';
|
|
21
23
|
import type { AuthLocalization } from '../../localization/auth-localization.js';
|
|
22
24
|
import AcceptInvitationCard from '../organization/accept-invitation-card.svelte';
|
|
@@ -128,6 +130,10 @@
|
|
|
128
130
|
<AuthCallback {redirectTo} />
|
|
129
131
|
{:else if view === 'SIGN_OUT'}
|
|
130
132
|
<SignOut />
|
|
133
|
+
{:else if view === 'VERIFY_EMAIL'}
|
|
134
|
+
<VerifyEmail {className} {classNames} {localization} />
|
|
135
|
+
{:else if view === 'MAGIC_LINK_SENT'}
|
|
136
|
+
<MagicLinkSent {className} {classNames} {localization} />
|
|
131
137
|
{:else if view === 'ACCEPT_INVITATION'}
|
|
132
138
|
<AcceptInvitationCard {localization} />
|
|
133
139
|
{:else}
|
|
@@ -201,7 +207,7 @@
|
|
|
201
207
|
socialLayout === 'grid' && 'grid grid-cols-2'
|
|
202
208
|
)}
|
|
203
209
|
>
|
|
204
|
-
{#each social?.providers || [] as providerName}
|
|
210
|
+
{#each social?.providers || [] as providerName (providerName)}
|
|
205
211
|
{@const provider = socialProviders.find((p) => p.provider === providerName)}
|
|
206
212
|
{#if provider}
|
|
207
213
|
<ProviderButton
|
|
@@ -217,7 +223,7 @@
|
|
|
217
223
|
{/if}
|
|
218
224
|
{/each}
|
|
219
225
|
|
|
220
|
-
{#each genericOAuth?.providers || [] as provider}
|
|
226
|
+
{#each genericOAuth?.providers || [] as provider (provider.providerId)}
|
|
221
227
|
<ProviderButton
|
|
222
228
|
{classNames}
|
|
223
229
|
{callbackURL}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
interface Props {
|
|
18
18
|
className?: string;
|
|
19
19
|
classNames?: AuthFormClassNames;
|
|
20
|
-
callbackURL?: string;
|
|
21
20
|
isSubmitting?: boolean;
|
|
22
21
|
localization?: Partial<AuthLocalization>;
|
|
23
22
|
otpSeparators?: 0 | 1 | 2;
|
|
@@ -28,7 +27,6 @@
|
|
|
28
27
|
let {
|
|
29
28
|
className,
|
|
30
29
|
classNames,
|
|
31
|
-
callbackURL,
|
|
32
30
|
isSubmitting: isSubmittingProp,
|
|
33
31
|
localization: localizationProp,
|
|
34
32
|
otpSeparators = 0,
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
const captchaHook = $derived(useCaptcha({ localization }));
|
|
46
46
|
|
|
47
47
|
// Local state for captcha binding
|
|
48
|
-
let captchaRef = $state<
|
|
48
|
+
let captchaRef = $state<unknown>(null);
|
|
49
49
|
|
|
50
50
|
// Sync captchaRef with the hook
|
|
51
51
|
$effect(() => {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
const basePath = config.basePath || '/auth';
|
|
76
76
|
const resetPasswordPath = config.viewPaths.RESET_PASSWORD || 'reset-password';
|
|
77
77
|
|
|
78
|
-
const fetchOptions:
|
|
78
|
+
const fetchOptions: Record<string, unknown> = {
|
|
79
79
|
throw: true,
|
|
80
80
|
headers: await captchaHook.getCaptchaHeaders('/forget-password')
|
|
81
81
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { createForm } from '@tanstack/svelte-form';
|
|
6
6
|
import { useCaptcha } from '../../../hooks/use-captcha.svelte';
|
|
7
7
|
import { useIsHydrated } from '../../../hooks/use-hydrated.svelte';
|
|
8
|
-
import { getAuthUIConfig
|
|
8
|
+
import { getAuthUIConfig } from '../../../context/auth-ui-config.svelte';
|
|
9
9
|
import { cn, getLocalizedError, getSearchParam, getFieldError } from '../../../utils/utils.js';
|
|
10
10
|
import type { AuthLocalization } from '../../../localization/auth-localization.js';
|
|
11
11
|
import Captcha from '../../captcha/captcha.svelte';
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
const { getCaptchaHeaders, resetCaptcha } = captchaHook;
|
|
42
42
|
|
|
43
43
|
// Local state for captcha binding
|
|
44
|
-
let captchaRef = $state<
|
|
44
|
+
let captchaRef = $state<unknown>(null);
|
|
45
45
|
|
|
46
46
|
// Sync captchaRef with the hook
|
|
47
47
|
$effect(() => {
|
|
@@ -57,7 +57,9 @@
|
|
|
57
57
|
localization: contextLocalization,
|
|
58
58
|
redirectTo: contextRedirectTo,
|
|
59
59
|
viewPaths,
|
|
60
|
-
toast
|
|
60
|
+
toast,
|
|
61
|
+
navigate,
|
|
62
|
+
magicLink: magicLinkConfig
|
|
61
63
|
} = config;
|
|
62
64
|
|
|
63
65
|
const localization = $derived({ ...contextLocalization, ...localizationProp });
|
|
@@ -105,7 +107,18 @@
|
|
|
105
107
|
fetchOptions
|
|
106
108
|
});
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
// Check if we should redirect to the sent page
|
|
111
|
+
const shouldRedirect =
|
|
112
|
+
typeof magicLinkConfig === 'object' && magicLinkConfig.redirectToSentPage;
|
|
113
|
+
|
|
114
|
+
if (shouldRedirect) {
|
|
115
|
+
// Navigate to the magic-link-sent page with the email as a query param
|
|
116
|
+
const magicLinkSentPath = `${basePath}/${viewPaths.MAGIC_LINK_SENT}`;
|
|
117
|
+
navigate(`${magicLinkSentPath}?email=${encodeURIComponent(value.email)}`);
|
|
118
|
+
} else {
|
|
119
|
+
// Show toast notification (original behavior)
|
|
120
|
+
toast.success(localization.MAGIC_LINK_EMAIL || 'Magic link sent to your email');
|
|
121
|
+
}
|
|
109
122
|
|
|
110
123
|
// Reset form
|
|
111
124
|
form.reset();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useCaptcha } from '../../../hooks/use-captcha.svelte';
|
|
7
7
|
import { useIsHydrated } from '../../../hooks/use-hydrated.svelte';
|
|
8
8
|
import { useOnSuccessTransition } from '../../../hooks/use-success-transition.svelte';
|
|
9
|
-
import { getAuthUIConfig
|
|
9
|
+
import { getAuthUIConfig } from '../../../context/auth-ui-config.svelte';
|
|
10
10
|
import {
|
|
11
11
|
cn,
|
|
12
12
|
getLocalizedError,
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
const { getCaptchaHeaders, resetCaptcha } = captchaHook;
|
|
52
52
|
|
|
53
53
|
// Local state for captcha binding
|
|
54
|
-
let captchaRef = $state<
|
|
54
|
+
let captchaRef = $state<unknown>(null);
|
|
55
55
|
|
|
56
56
|
// Sync captchaRef with the hook
|
|
57
57
|
$effect(() => {
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
const basePath = config.basePath;
|
|
67
67
|
const baseURL = config.baseURL;
|
|
68
68
|
const credentials = config.credentials;
|
|
69
|
+
const emailVerification = config.emailVerification;
|
|
69
70
|
const nameRequired = config.nameRequired;
|
|
70
71
|
const persistClient = config.persistClient;
|
|
71
72
|
const contextRedirectTo = config.redirectTo;
|
|
@@ -89,7 +90,7 @@
|
|
|
89
90
|
const { getCaptchaHeaders, resetCaptcha } = captchaHook;
|
|
90
91
|
|
|
91
92
|
// Local state for captcha binding
|
|
92
|
-
let captchaRef = $state<
|
|
93
|
+
let captchaRef = $state<unknown>(null);
|
|
93
94
|
|
|
94
95
|
// Sync captchaRef with the hook
|
|
95
96
|
$effect(() => {
|
|
@@ -277,8 +278,7 @@
|
|
|
277
278
|
|
|
278
279
|
// Sign up function
|
|
279
280
|
async function signUp(values: z.infer<typeof formSchema>) {
|
|
280
|
-
const { email, password, name, username,
|
|
281
|
-
values;
|
|
281
|
+
const { email, password, name, username, image, ...additionalFieldValues } = values;
|
|
282
282
|
|
|
283
283
|
try {
|
|
284
284
|
// Validate additional fields with custom validators if provided
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
if (!additionalField?.validate) continue;
|
|
288
288
|
|
|
289
289
|
if (typeof value === 'string' && !(await additionalField.validate(value))) {
|
|
290
|
-
form.setFieldMeta(field as
|
|
290
|
+
form.setFieldMeta(field as never, (prev) => ({
|
|
291
291
|
...prev,
|
|
292
292
|
errorMap: {
|
|
293
293
|
...prev.errorMap,
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
// Prepare fetch options with captcha headers
|
|
302
|
-
const fetchOptions:
|
|
302
|
+
const fetchOptions: Record<string, unknown> = {
|
|
303
303
|
throw: true,
|
|
304
304
|
headers: await getCaptchaHeaders('/sign-up/email')
|
|
305
305
|
};
|
|
@@ -314,13 +314,22 @@
|
|
|
314
314
|
additionalParams.image = image;
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
// Determine the callback URL for email verification
|
|
318
|
+
// If redirectToVerifyPage is true, redirect to verify-email page after verification
|
|
319
|
+
let signUpCallbackURL = getCallbackURL();
|
|
320
|
+
if (emailVerification?.redirectToVerifyPage) {
|
|
321
|
+
const origin = baseURL || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
322
|
+
const verifyEmailPath = `${basePath}/${viewPaths.VERIFY_EMAIL}`;
|
|
323
|
+
signUpCallbackURL = `${origin}${verifyEmailPath}?verified=true&email=${encodeURIComponent(email as string)}`;
|
|
324
|
+
}
|
|
325
|
+
|
|
317
326
|
const data = await authClient.signUp.email({
|
|
318
327
|
email: email as string,
|
|
319
328
|
password: password as string,
|
|
320
329
|
name: (name as string) || '',
|
|
321
330
|
...additionalParams,
|
|
322
331
|
...additionalFieldValues,
|
|
323
|
-
callbackURL:
|
|
332
|
+
callbackURL: signUpCallbackURL,
|
|
324
333
|
fetchOptions
|
|
325
334
|
});
|
|
326
335
|
|
|
@@ -329,9 +338,14 @@
|
|
|
329
338
|
// User is signed in immediately
|
|
330
339
|
await onSuccess();
|
|
331
340
|
} else {
|
|
332
|
-
// Email verification required
|
|
333
|
-
|
|
334
|
-
|
|
341
|
+
// Email verification required - redirect to verify-email view
|
|
342
|
+
// Note: This handles token-based email verification (default).
|
|
343
|
+
// If the server uses emailOTP with overrideDefaultEmailVerification,
|
|
344
|
+
// users receive OTP codes via email instead of verification links.
|
|
345
|
+
const url = new URL(window.location.href);
|
|
346
|
+
url.searchParams.set('email', email as string); // URLSearchParams handles encoding automatically
|
|
347
|
+
navigate(`${basePath}/${viewPaths.VERIFY_EMAIL}?${url.searchParams.toString()}`);
|
|
348
|
+
// Don't show a toast here since the verify-email view will show all needed info
|
|
335
349
|
}
|
|
336
350
|
} catch (error) {
|
|
337
351
|
toast.error(getLocalizedError({ error, localization }));
|
|
@@ -368,7 +382,7 @@
|
|
|
368
382
|
|
|
369
383
|
// Create validators for additional fields dynamically
|
|
370
384
|
function getAdditionalFieldValidator(field: string) {
|
|
371
|
-
return (formSchema.shape as
|
|
385
|
+
return (formSchema.shape as Record<string, z.ZodTypeAny>)[field];
|
|
372
386
|
}
|
|
373
387
|
|
|
374
388
|
// Combine isSubmitting states
|
|
@@ -416,18 +430,29 @@
|
|
|
416
430
|
<div class="flex items-center gap-4">
|
|
417
431
|
<DropdownMenu.Root>
|
|
418
432
|
<DropdownMenu.Trigger class="size-fit rounded-full">
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
433
|
+
{#snippet child({ props })}
|
|
434
|
+
<Button
|
|
435
|
+
{...props}
|
|
436
|
+
class="size-fit rounded-full"
|
|
437
|
+
size="icon"
|
|
438
|
+
type="button"
|
|
439
|
+
variant="ghost"
|
|
440
|
+
disabled={uploadingAvatar}
|
|
441
|
+
>
|
|
442
|
+
<UserAvatar
|
|
443
|
+
isPending={uploadingAvatar}
|
|
444
|
+
className="size-16"
|
|
445
|
+
user={avatarImage
|
|
446
|
+
? {
|
|
447
|
+
name: form.getFieldValue('name') as string,
|
|
448
|
+
email: form.getFieldValue('email') as string,
|
|
449
|
+
image: avatarImage
|
|
450
|
+
}
|
|
451
|
+
: null}
|
|
452
|
+
{localization}
|
|
453
|
+
/>
|
|
454
|
+
</Button>
|
|
455
|
+
{/snippet}
|
|
431
456
|
</DropdownMenu.Trigger>
|
|
432
457
|
|
|
433
458
|
<DropdownMenu.Content align="start">
|
|
@@ -607,11 +632,11 @@
|
|
|
607
632
|
|
|
608
633
|
<!-- Additional Fields -->
|
|
609
634
|
{#if signUpFields}
|
|
610
|
-
{#each signUpFields.filter((field) => field !== 'name' && field !== 'image') as field}
|
|
635
|
+
{#each signUpFields.filter((field) => field !== 'name' && field !== 'image') as field (field)}
|
|
611
636
|
{@const additionalField = additionalFields?.[field]}
|
|
612
637
|
{#if additionalField}
|
|
613
638
|
<form.Field
|
|
614
|
-
name={field as
|
|
639
|
+
name={field as never}
|
|
615
640
|
validators={{ onChange: getAdditionalFieldValidator(field) }}
|
|
616
641
|
>
|
|
617
642
|
{#snippet children(fieldState)}
|
|
@@ -623,7 +648,7 @@
|
|
|
623
648
|
id={field}
|
|
624
649
|
checked={fieldState.state.value as boolean}
|
|
625
650
|
onCheckedChange={(checked) => {
|
|
626
|
-
fieldState.handleChange(checked as
|
|
651
|
+
fieldState.handleChange(checked as never);
|
|
627
652
|
}}
|
|
628
653
|
disabled={isSubmitting}
|
|
629
654
|
/>
|
|
@@ -6,11 +6,7 @@
|
|
|
6
6
|
import SendIcon from '@lucide/svelte/icons/send';
|
|
7
7
|
import { useIsHydrated } from '../../../hooks/use-hydrated.svelte';
|
|
8
8
|
import { useOnSuccessTransition } from '../../../hooks/use-success-transition.svelte';
|
|
9
|
-
import {
|
|
10
|
-
getAuthUIConfig,
|
|
11
|
-
getAuthClient,
|
|
12
|
-
getLocalization
|
|
13
|
-
} from '../../../context/auth-ui-config.svelte';
|
|
9
|
+
import { getAuthUIConfig, getAuthClient } from '../../../context/auth-ui-config.svelte';
|
|
14
10
|
import { cn, getLocalizedError, getSearchParam, getFieldError } from '../../../utils/utils.js';
|
|
15
11
|
import type { AuthLocalization } from '../../../localization/auth-localization.js';
|
|
16
12
|
import type { User } from '../../../types/index.js';
|