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?
+
+ Enter the 6-digit one-time code generated by the app
+
+
+
+ (showDelete = false)}>Cancel
+ Delete
+
+
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}
+
+ {/await}
+
+
+
+ Enter the 6-digit one-time code generated by the app
+
+
+ {/key}
+
+ (showSetup = false)}>Cancel
+ Continue
+
+
+
+
+ {#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}
+
+ (showRecoveryCodes = false)}>Close
+
+
+
+
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 @@
+
+
+
+
+
+
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
+
+
+
+
+
+ Forgot Password?
+
+
+
+ Sign Up
+
+
+
+