Manually adding totp error and converting to use shadcn form on login.

This commit is contained in:
Bradley Shellnut 2024-04-08 17:47:54 -07:00
parent 8b48466c7e
commit 389fddc32e
3 changed files with 38 additions and 24 deletions

View file

@ -26,4 +26,4 @@ export const signInSchema = z.object({
.string({ required_error: 'Password is required' }) .string({ required_error: 'Password is required' })
.trim(), .trim(),
totpToken: z.string().trim().min(6).max(6).optional() totpToken: z.string().trim().min(6).max(6).optional()
}) });

View file

@ -84,11 +84,10 @@ export const actions: Actions = {
.onConflictDoNothing(); .onConflictDoNothing();
if (user?.two_factor_enabled && user?.two_factor_secret && !form?.data?.totpToken) { if (user?.two_factor_enabled && user?.two_factor_secret && !form?.data?.totpToken) {
return setError( return fail(400, {
form, form,
'totpToken', twoFactorRequired: true,
'Two factor authentication is enabled. Please enter your 2FA code.', });
);
} else if (user?.two_factor_enabled && user?.two_factor_secret && form?.data?.totpToken) { } else if (user?.two_factor_enabled && user?.two_factor_secret && form?.data?.totpToken) {
console.log('totpToken', form.data.totpToken); console.log('totpToken', form.data.totpToken);
const validOTP = await new TOTPController().verify( const validOTP = await new TOTPController().verify(
@ -96,8 +95,12 @@ export const actions: Actions = {
decodeHex(user.two_factor_secret) decodeHex(user.two_factor_secret)
); );
console.log('validOTP', validOTP); console.log('validOTP', validOTP);
form.errors.totpToken = ['Invalid TOTP code'];
if (!validOTP) { if (!validOTP) {
return setError(form, 'totpToken', 'Invalid 2FA code'); return fail(400, {
form,
twoFactorRequired: true,
});
} }
} }
console.log('ip', locals.ip); console.log('ip', locals.ip);

View file

@ -4,6 +4,7 @@
import * as flashModule from 'sveltekit-flash-message/client'; import * as flashModule from 'sveltekit-flash-message/client';
import { AlertCircle } from "lucide-svelte"; import { AlertCircle } from "lucide-svelte";
import { signInSchema } from '$lib/validations/auth'; import { signInSchema } from '$lib/validations/auth';
import * as Form from '$lib/components/ui/form';
import { Label } from '$components/ui/label'; import { Label } from '$components/ui/label';
import { Input } from '$components/ui/input'; import { Input } from '$components/ui/input';
import { Button } from '$components/ui/button'; import { Button } from '$components/ui/button';
@ -11,7 +12,9 @@
import { boredState } from '$lib/stores/boredState.js'; import { boredState } from '$lib/stores/boredState.js';
export let data; export let data;
const { form, errors, enhance } = superForm(data.form, { export let form;
const superLoginForm = superForm(data.form, {
onSubmit: () => boredState.update((n) => ({ ...n, loading: true })), onSubmit: () => boredState.update((n) => ({ ...n, loading: true })),
onResult: () => boredState.update((n) => ({ ...n, loading: false })), onResult: () => boredState.update((n) => ({ ...n, loading: false })),
flashMessage: { flashMessage: {
@ -30,6 +33,8 @@
validationMethod: 'oninput', validationMethod: 'oninput',
delayMs: 0, delayMs: 0,
}); });
const { form: loginForm, errors, enhance } = superLoginForm;
</script> </script>
<svelte:head> <svelte:head>
@ -43,24 +48,30 @@
> >
Log into your account Log into your account
</h2> </h2>
<Label for="username">Username</Label> <Form.Field form={superLoginForm} name="username">
<Input type="text" id="username" name="username" placeholder="Username" autocomplete="username" data-invalid={$errors.username} bind:value={$form.username} required /> <Form.Control let:attrs>
<Label for="password">Password</Label> <Form.Label for="username">Username</Form.Label>
<Input type="password" id="password" name="password" placeholder="Password" autocomplete="password" data-invalid={$errors.password} bind:value={$form.password} required /> <Input {...attrs} autocomplete="username" bind:value={$loginForm.username} />
{#if $errors.totpToken} </Form.Control>
<Label for="totpToken">2FA Code</Label> <Form.FieldErrors />
<Input type="text" id="totpToken" name="totpToken" placeholder="2FA Code" autocomplete="one-time-code" data-invalid={$errors.totpToken} bind:value={$form.totpToken} /> </Form.Field>
{/if} <Form.Field form={superLoginForm} name="password">
<Button type="submit">Login</Button> <Form.Control let:attrs>
{#if $errors._errors} <Form.Label for="password">Password</Form.Label>
<Alert.Root variant="destructive"> <Input {...attrs} autocomplete="current-password" type="password" bind:value={$loginForm.password} />
<AlertCircle class="h-4 w-4" /> </Form.Control>
<Alert.Title>Error</Alert.Title> <Form.FieldErrors />
<Alert.Description> </Form.Field>
{$errors._errors} {#if form?.twoFactorRequired}
</Alert.Description> <Form.Field form={superLoginForm} name="totpToken">
</Alert.Root> <Form.Control let:attrs>
<Form.Label for="totpToken">2FA Code</Form.Label>
<Input {...attrs} autocomplete="one-time-code" bind:value={$loginForm.totpToken} />
</Form.Control>
<Form.FieldErrors />
</Form.Field>
{/if} {/if}
<Form.Button>Login</Form.Button>
<p class="px-8 text-center text-sm text-muted-foreground"> <p class="px-8 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our By clicking continue, you agree to our
<a href="/terms" class="underline underline-offset-4 hover:text-primary"> <a href="/terms" class="underline underline-offset-4 hover:text-primary">