mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
152 lines
5.9 KiB
Svelte
152 lines
5.9 KiB
Svelte
<script context="module" lang="ts">
|
|
let inputDigitFields: InputDigits;
|
|
|
|
export async function verify(challenge: Models.MfaChallenge, code: string) {
|
|
try {
|
|
if (challenge == null) {
|
|
challenge = await sdk.forConsole.account.createMfaChallenge(
|
|
AuthenticationFactor.Totp
|
|
);
|
|
}
|
|
await sdk.forConsole.account.updateMfaChallenge(challenge.$id, code);
|
|
await invalidate(Dependencies.ACCOUNT);
|
|
trackEvent(Submit.AccountCreate);
|
|
} catch (error) {
|
|
inputDigitFields?.clearInputsAndRefocus();
|
|
trackError(error, Submit.AccountCreate);
|
|
throw error;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { invalidate } from '$app/navigation';
|
|
import { Button, FormItem, FormList, InputDigits, InputText } from '$lib/elements/forms';
|
|
import { sdk } from '$lib/stores/sdk';
|
|
import { Dependencies } from '$lib/constants';
|
|
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
|
import { AuthenticationFactor, type Models } from '@appwrite.io/console';
|
|
import { addNotification } from '$lib/stores/notifications';
|
|
|
|
export let factors: Models.MfaFactors & { recoveryCode: boolean };
|
|
/** If true, the form will be submitted automatically when the code is entered. */
|
|
export let autoSubmit: boolean = true;
|
|
export let showVerifyButton: boolean = true;
|
|
export let disabled: boolean = false;
|
|
export let challenge: Models.MfaChallenge;
|
|
export let code: string;
|
|
|
|
let challengeType: AuthenticationFactor;
|
|
|
|
const enabledFactors = Object.entries(factors).filter(([_, enabled]) => enabled);
|
|
|
|
async function createChallenge(factor: AuthenticationFactor) {
|
|
disabled = true;
|
|
challengeType = factor;
|
|
try {
|
|
challenge = await sdk.forConsole.account.createMfaChallenge(factor);
|
|
} catch (error) {
|
|
addNotification({
|
|
type: 'error',
|
|
message: error.message
|
|
});
|
|
} finally {
|
|
disabled = false;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<FormList>
|
|
{#if challengeType == AuthenticationFactor.Recoverycode}
|
|
<p>
|
|
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 />
|
|
{:else}
|
|
{#if factors.totp && (challengeType == AuthenticationFactor.Totp || challengeType == null)}
|
|
<p>Enter below a 6-digit one-time code generated by your authentication app.</p>
|
|
{:else if challengeType == AuthenticationFactor.Email}
|
|
<p>A 6-digit verification code was sent to your email, enter it below.</p>
|
|
{:else if challengeType == AuthenticationFactor.Phone}
|
|
<p>A 6-digit verification code was sent to your phone, enter it below.</p>
|
|
{/if}
|
|
<InputDigits
|
|
bind:value={code}
|
|
required
|
|
autofocus
|
|
{autoSubmit}
|
|
bind:this={inputDigitFields} />
|
|
{/if}
|
|
{#if showVerifyButton}
|
|
<FormItem>
|
|
<Button fullWidth submit {disabled}>Verify</Button>
|
|
</FormItem>
|
|
{/if}
|
|
{#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>
|