diff --git a/src/lib/components/eventModal.svelte b/src/lib/components/eventModal.svelte index 18440bea2c..c125ddacbf 100644 --- a/src/lib/components/eventModal.svelte +++ b/src/lib/components/eventModal.svelte @@ -332,7 +332,7 @@ {:else}
- diff --git a/src/lib/elements/forms/button.svelte b/src/lib/elements/forms/button.svelte index 70b21cec8a..0803c76503 100644 --- a/src/lib/elements/forms/button.svelte +++ b/src/lib/elements/forms/button.svelte @@ -15,6 +15,7 @@ export let disabled = false; export let external = false; export let href: string = null; + export let download: string = undefined; export let fullWidth = false; export let fullWidthMobile = false; export let ariaLabel: string = null; @@ -63,6 +64,7 @@ on:click on:click={track} {href} + {download} target={external ? '_blank' : ''} rel={external ? 'noopener noreferrer' : ''} class={resolvedClasses} diff --git a/src/lib/images/qr.svg b/src/lib/images/qr.svg new file mode 100644 index 0000000000..e318d20f89 --- /dev/null +++ b/src/lib/images/qr.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 1b7b099e2d..1faf114113 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -34,13 +34,19 @@ export const load: LayoutLoad = async ({ depends, url }) => { '/auth/oauth2/success', '/auth/oauth2/failure', '/card', - '/hackathon' + '/hackathon', + '/mfa' ]; + const redirectUrl = url.pathname && url.pathname !== '/' ? `redirect=${url.pathname}` : ''; + const path = url.search ? `${url.search}&${redirectUrl}` : `?${redirectUrl}`; + + if (error.type === 'user_more_factors_required') { + if (url.pathname === '/mfa') return; + throw redirect(303, `/mfa${path}`); + } + if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) { - const redirectUrl = - url.pathname && url.pathname !== '/' ? `redirect=${url.pathname}` : ''; - const path = url.search ? `${url.search}&${redirectUrl}` : `?${redirectUrl}`; throw redirect(303, `/login${path}`); } } diff --git a/src/routes/console/account/+page.svelte b/src/routes/console/account/+page.svelte index 6eb5ab19ac..1006770d86 100644 --- a/src/routes/console/account/+page.svelte +++ b/src/routes/console/account/+page.svelte @@ -4,11 +4,13 @@ import UpdateName from './updateName.svelte'; import UpdateEmail from './updateEmail.svelte'; import DeleteAccount from './deleteAccount.svelte'; + import UpdateMfa from './updateMfa.svelte'; + diff --git a/src/routes/console/account/deleteMfa.svelte b/src/routes/console/account/deleteMfa.svelte new file mode 100644 index 0000000000..3115e73cbf --- /dev/null +++ b/src/routes/console/account/deleteMfa.svelte @@ -0,0 +1,59 @@ + + + +

Are you sure you want to delete this authentication method from your account?

+
+ + +
+ + + + +
diff --git a/src/routes/console/account/mfa.svelte b/src/routes/console/account/mfa.svelte new file mode 100644 index 0000000000..e00cec76f5 --- /dev/null +++ b/src/routes/console/account/mfa.svelte @@ -0,0 +1,163 @@ + + + + {#key showSetup} +

+ Install an authenticator app on your mobile device, open it and scan the provided QR + code or enter it manually. +

+
+ {#await addAuthenticator()} + + {:then qr} + MFA QR Code + {/await} +
+
+
+ + +
+ {/key} + + + + +
+ + + {#if provider} + {@const formattedBackupCodes = provider.backups.join('\n')} + + + It is highly recommended to securely store your recovery codes + +

+ Use security codes for emergency sign-ins in case you've lost access to your mobile + device. Each recovery code can only be used once, but you can re-generate a new set + of 6 codes anytime. +

+
+
+
    +
  • + +
  • +
  • + + + +
  • +
+
+ + + {#each provider.backups as code} + + + {code} + + + + + + + {/each} + +
+ {/if} + + + +
+ + diff --git a/src/routes/console/account/updateMfa.svelte b/src/routes/console/account/updateMfa.svelte new file mode 100644 index 0000000000..40a15c10f5 --- /dev/null +++ b/src/routes/console/account/updateMfa.svelte @@ -0,0 +1,106 @@ + + +
+ + Multi-factor authentication + + + + + +

Enhance the security of your account by adding authentication factors.

+ {#if mfa} + {#if $user.totp} + + + Authenticator + + + + + + TOTP (One-time code) + + + + + + +
+ + {:else} + (showSetup = true)}> +

Add authentication factor

+
+ {/if} + {/if} +
+ + + + +
+
+ + + diff --git a/src/routes/console/project-[project]/messaging/providers/store.ts b/src/routes/console/project-[project]/messaging/providers/store.ts index dc39148491..23ff8fc0ec 100644 --- a/src/routes/console/project-[project]/messaging/providers/store.ts +++ b/src/routes/console/project-[project]/messaging/providers/store.ts @@ -26,7 +26,15 @@ type ProvidersMap = { configure: { label: string; name: string; - type: 'text' | 'password' | 'phone' | 'email' | 'domain' | 'file' | 'switch' | 'select'; + type: + | 'text' + | 'password' + | 'phone' + | 'email' + | 'domain' + | 'file' + | 'switch' + | 'select'; placeholder?: string; description?: string; popover?: string[]; diff --git a/src/routes/console/project-[project]/messaging/wizard/step1.svelte b/src/routes/console/project-[project]/messaging/wizard/step1.svelte index aa8da71b92..a7df944958 100644 --- a/src/routes/console/project-[project]/messaging/wizard/step1.svelte +++ b/src/routes/console/project-[project]/messaging/wizard/step1.svelte @@ -9,7 +9,7 @@ async function beforeSubmit() {} - const createMessage = (providerText:string) => { + const createMessage = (providerText: string) => { const vowels = ['a', 'e', 'i', 'o', 'u']; const firstLetter = providerText.toLowerCase().charAt(0); const lastLetter = providerText.toLowerCase().charAt(providerText.length - 1); diff --git a/src/routes/mfa/+page.svelte b/src/routes/mfa/+page.svelte new file mode 100644 index 0000000000..a861465148 --- /dev/null +++ b/src/routes/mfa/+page.svelte @@ -0,0 +1,76 @@ + + + + Verify - Appwrite + + + + Verify your identity + +
+ + + + + + +
+
+ + + + +