Files
drop/pages/mfa/setup/webauthn.vue
DecDuck 63ac2b8ffc Depot API & v4 (#298)
* feat: nginx + torrential basics & services system

* fix: lint + i18n

* fix: update torrential to remove openssl

* feat: add torrential to Docker build

* feat: move to self hosted runner

* fix: move off self-hosted runner

* fix: update nginx.conf

* feat: torrential cache invalidation

* fix: update torrential for cache invalidation

* feat: integrity check task

* fix: lint

* feat: move to version ids

* fix: client fixes and client-side checks

* feat: new depot apis and version id fixes

* feat: update torrential

* feat: droplet bump and remove unsafe update functions

* fix: lint

* feat: v4 featureset: emulators, multi-launch commands

* fix: lint

* fix: mobile ui for game editor

* feat: launch options

* fix: lint

* fix: remove axios, use $fetch

* feat: metadata and task api improvements

* feat: task actions

* fix: slight styling issue

* feat: fix style and lints

* feat: totp backend routes

* feat: oidc groups

* fix: update drop-base

* feat: creation of passkeys & totp

* feat: totp signin

* feat: webauthn mfa/signin

* feat: launch selecting ui

* fix: manually running tasks

* feat: update add company game modal to use new SelectorGame

* feat: executor selector

* fix(docker): update rust to rust nightly for torrential build (#305)

* feat: new version ui

* feat: move package lookup to build time to allow for deno dev

* fix: lint

* feat: localisation cleanup

* feat: apply localisation cleanup

* feat: potential i18n refactor logic

* feat: remove args from commands

* fix: lint

* fix: lockfile

---------

Co-authored-by: Aden Lindsay <140392385+AdenMGB@users.noreply.github.com>
2026-01-13 15:32:39 +11:00

134 lines
3.5 KiB
Vue

<template>
<div
class="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8"
>
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<KeyIcon class="text-blue-600 mx-auto h-10 w-auto" />
<h2
class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-white"
>
Create a passkey
</h2>
<p class="text-sm text-center text-zinc-400">
WebAuthn, or passkeys, allow you to sign in or complete 2FA with
biometrics or hardware security devices.
</p>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form
class="space-y-6"
action="#"
method="POST"
@submit.prevent="attemptPasskeyWrapper"
>
<div>
<label for="name" class="block text-sm/6 font-medium text-gray-100"
>Name</label
>
<div class="mt-2">
<input
id="name"
v-model="name"
type="text"
name="name"
required
placeholder="My New Passkey"
class="block w-full rounded-md bg-white/5 px-3 py-1.5 text-base text-white outline-1 -outline-offset-1 outline-white/10 placeholder:text-gray-500 focus:outline-2 focus:-outline-offset-2 focus:outline-blue-500 sm:text-sm/6"
/>
</div>
</div>
<div>
<LoadingButton :disabled="disabled" :loading="loading" class="w-full">
Create
</LoadingButton>
</div>
<div
v-if="error"
class="mt-4 rounded-md bg-red-600/10 p-4 max-w-sm mx-auto"
>
<div class="flex">
<div class="flex-shrink-0">
<XCircleIcon class="h-5 w-5 text-red-600" aria-hidden="true" />
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-600">
{{ error }}
</h3>
</div>
</div>
</div>
</form>
</div>
</div>
</template>
<script setup lang="ts">
import { KeyIcon, XCircleIcon } from "@heroicons/vue/24/outline";
import type { FetchError } from "ofetch";
import { startRegistration } from "@simplewebauthn/browser";
const router = useRouter();
const name = ref("");
const disabled = computed(() => !name.value);
const loading = ref(false);
const error = ref<string | undefined>();
useHead({
title: "Create a passkey",
});
async function attemptPasskeyWrapper() {
loading.value = true;
try {
await attemptPasskey();
} catch (e) {
console.error(e);
error.value = (e as FetchError)?.data?.message ?? e;
}
loading.value = false;
}
async function attemptPasskey() {
if (!window.PublicKeyCredential)
throw createError({
statusCode: 400,
message: "Browser does not support WebAuthn",
fatal: true,
});
const optionsJSON = await $dropFetch("/api/v1/user/mfa/webauthn/start", {
method: "POST",
body: {
name: name.value,
},
});
let attResp;
try {
// Pass the options to the authenticator and wait for a response
attResp = await startRegistration({ optionsJSON });
} catch {
throw createError({
statusCode: 400,
message: "WebAuthn request cancelled.",
});
}
if (!attResp)
throw createError({
statusCode: 400,
message: "WebAuthn request cancelled.",
});
await $dropFetch("/api/v1/user/mfa/webauthn/finish", {
method: "POST",
body: attResp,
});
router.push("/mfa/setup/successful");
}
</script>