mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat(mfa): show email, phone, and recovery options for MFA verification
Show the appropriate text and buttons depending on what factors are enabled and what challenge has been created.
This commit is contained in:
+110
-9
@@ -1,24 +1,50 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { Button, Form, FormItem, FormList } from '$lib/elements/forms';
|
||||
import { Button, Form, FormItem, FormList, InputDigits, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Unauthenticated } from '$lib/layout';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { page } from '$app/stores';
|
||||
import { AuthenticationFactor } from '@appwrite.io/console';
|
||||
import InputDigits from '$lib/elements/forms/inputDigits.svelte';
|
||||
import { AuthenticationFactor, type Models } from '@appwrite.io/console';
|
||||
|
||||
let code: string, disabled: boolean;
|
||||
export let data;
|
||||
|
||||
let code: string;
|
||||
let disabled: boolean;
|
||||
let challenge: Models.MfaChallenge;
|
||||
let challengeType: AuthenticationFactor;
|
||||
|
||||
const factors = data.factors as Models.MfaFactors & { recoveryCode: boolean };
|
||||
const enabledFactors = Object.entries(factors).filter(([_, enabled]) => enabled);
|
||||
|
||||
async function createChallenge(factor: AuthenticationFactor) {
|
||||
challengeType = factor;
|
||||
challenge = await sdk.forConsole.account.createMfaChallenge(factor);
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const enabledNonRecoveryFactors = enabledFactors.filter(
|
||||
([factor, _]) => factor != 'recoveryCode'
|
||||
);
|
||||
if (enabledNonRecoveryFactors.length == 1) {
|
||||
if (factors.phone) {
|
||||
createChallenge(AuthenticationFactor.Phone);
|
||||
} else if (factors.email) {
|
||||
createChallenge(AuthenticationFactor.Email);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function verify() {
|
||||
try {
|
||||
disabled = true;
|
||||
const challenge = await sdk.forConsole.account.createMfaChallenge(
|
||||
AuthenticationFactor.Totp
|
||||
);
|
||||
if (challenge == null) {
|
||||
await createChallenge(AuthenticationFactor.Totp);
|
||||
}
|
||||
await sdk.forConsole.account.updateMfaChallenge(challenge.$id, code);
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
trackEvent(Submit.AccountCreate);
|
||||
@@ -53,11 +79,86 @@
|
||||
<svelte:fragment>
|
||||
<Form onSubmit={verify}>
|
||||
<FormList>
|
||||
<p>Enter below a 6-digit one-time code generated by your authentication app.</p>
|
||||
<InputDigits bind:value={code} required autofocus />
|
||||
{#if factors.totp && (challengeType == AuthenticationFactor.Totp || challengeType == null)}
|
||||
<p class="body-text-1">
|
||||
Enter below a 6-digit one-time code generated by your authentication app.
|
||||
</p>
|
||||
<InputDigits bind:value={code} required autofocus />
|
||||
{:else if challengeType == AuthenticationFactor.Email}
|
||||
<p class="body-text-1">
|
||||
A 6-digit verification code was sent to your email, enter it below.
|
||||
</p>
|
||||
<InputDigits bind:value={code} required autofocus />
|
||||
{:else if challengeType == AuthenticationFactor.Phone}
|
||||
<p class="body-text-1">
|
||||
A 6-digit verification code was sent to your phone, enter it below.
|
||||
</p>
|
||||
<InputDigits bind:value={code} required autofocus />
|
||||
{:else if challengeType == AuthenticationFactor.Recoverycode}
|
||||
<p class="body-text-1">
|
||||
Enter below one of the recovery codes you received when enabling MFA for
|
||||
this account.
|
||||
</p>
|
||||
<InputText id="recovery-code" bind:value={code} required autofocus />
|
||||
{/if}
|
||||
<FormItem>
|
||||
<Button fullWidth submit {disabled}>Verify</Button>
|
||||
</FormItem>
|
||||
{#if enabledFactors.length > 1}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
{#if factors.totp && challengeType != null && challengeType != AuthenticationFactor.Totp}
|
||||
<FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Totp)}>
|
||||
<span
|
||||
class="icon-device-mobile u-font-size-20"
|
||||
aria-hidden="true" />
|
||||
<span class="text">Authenticator app</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
{/if}
|
||||
{#if factors.email && challengeType != AuthenticationFactor.Email}
|
||||
<FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Email)}>
|
||||
<span class="icon-mail u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Email verification</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
{/if}
|
||||
{#if factors.phone && challengeType != AuthenticationFactor.Phone}
|
||||
<FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Phone)}>
|
||||
<span class="icon-chat-alt u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Phone verification</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
{/if}
|
||||
{#if factors.recoveryCode && challengeType != AuthenticationFactor.Recoverycode}
|
||||
<FormItem>
|
||||
<Button
|
||||
text
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() =>
|
||||
createChallenge(AuthenticationFactor.Recoverycode)}>
|
||||
<span class="text">Use recovery code</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</FormList>
|
||||
</Form>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
return {
|
||||
factors: await sdk.forConsole.account.listMfaFactors()
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user