Reset dialog UX improved.

This commit is contained in:
Leo Vasanko
2025-10-04 18:25:40 -06:00
parent 59e7e40128
commit 876215f1c1
2 changed files with 9 additions and 6 deletions

View File

@@ -10,7 +10,7 @@
<div class="view-content"> <div class="view-content">
<div class="surface surface--tight" style="max-width: 560px; margin: 0 auto; width: 100%;"> <div class="surface surface--tight" style="max-width: 560px; margin: 0 auto; width: 100%;">
<header class="view-header" style="text-align: center;"> <header class="view-header" style="text-align: center;">
<h1>🔑 Complete Your Passkey Setup</h1> <h1>🔑 Registration</h1>
<p class="view-lede"> <p class="view-lede">
{{ subtitleMessage }} {{ subtitleMessage }}
</p> </p>
@@ -38,13 +38,11 @@
<input <input
type="text" type="text"
v-model="displayName" v-model="displayName"
:placeholder="namePlaceholder"
:disabled="loading" :disabled="loading"
maxlength="64" maxlength="64"
@keyup.enter="registerPasskey" @keyup.enter="registerPasskey"
/> />
</label> </label>
<p>Click below to finish {{ sessionDescriptor }}.</p>
<button <button
class="btn-primary" class="btn-primary"
:disabled="loading" :disabled="loading"
@@ -81,11 +79,10 @@ const errorMessage = ref('')
let statusTimer = null let statusTimer = null
const sessionDescriptor = computed(() => userInfo.value?.session_type || 'your enrollment') const sessionDescriptor = computed(() => userInfo.value?.session_type || 'your enrollment')
const namePlaceholder = computed(() => userInfo.value?.user?.user_name || 'Your name')
const subtitleMessage = computed(() => { const subtitleMessage = computed(() => {
if (initializing.value) return 'Preparing your secure enrollment…' if (initializing.value) return 'Preparing your secure enrollment…'
if (!canRegister.value) return 'This reset link is no longer valid.' if (!canRegister.value) return 'This reset link is no longer valid.'
return `Finish setting up a passkey for ${userInfo.value?.user?.user_name || 'your account'}.` return `Finish up ${sessionDescriptor.value}. You may edit the name below if needed, and it will be saved to your passkey.`
}) })
const basePath = computed(() => uiBasePath()) const basePath = computed(() => uiBasePath())
@@ -128,6 +125,7 @@ async function fetchUserInfo() {
return return
} }
userInfo.value = await res.json() userInfo.value = await res.json()
displayName.value = userInfo.value?.user?.user_name || ''
} catch (error) { } catch (error) {
console.error('Failed to load user info', error) console.error('Failed to load user info', error)
const message = 'We could not load your reset details. Try refreshing the page.' const message = 'We could not load your reset details. Try refreshing the page.'

View File

@@ -441,13 +441,18 @@ async def admin_create_user_registration_link(
and f"auth:org:{org_uuid}" not in ctx.role.permissions and f"auth:org:{org_uuid}" not in ctx.role.permissions
): ):
raise HTTPException(status_code=403, detail="Insufficient permissions") raise HTTPException(status_code=403, detail="Insufficient permissions")
# Check if user has existing credentials
credentials = await db.instance.get_credentials_by_user_uuid(user_uuid)
token_type = "user registration" if not credentials else "account recovery"
token = passphrase.generate() token = passphrase.generate()
expiry = reset_expires() expiry = reset_expires()
await db.instance.create_reset_token( await db.instance.create_reset_token(
user_uuid=user_uuid, user_uuid=user_uuid,
key=tokens.reset_key(token), key=tokens.reset_key(token),
expiry=expiry, expiry=expiry,
token_type="device addition", token_type=token_type,
) )
url = hostutil.reset_link_url( url = hostutil.reset_link_url(
token, request.url.scheme, request.headers.get("host") token, request.url.scheme, request.headers.get("host")