Files
WRNexusJS/packages/auth/components/TwoFactorChallenge.wrn
T

70 lines
4.4 KiB
Plaintext

import { Button, Input, PinInput, Radio, Checkbox } from "@wrnexus/ui"
component TwoFactorChallenge {
props {
action = "/api/auth/mfa/complete"
schema = "auth-mfa"
otpAction = "/api/auth/mfa/otp"
mfaToken = ""
challengeId = ""
method = "totp"
returnTo = "/"
title = "Two-step verification"
description = "Choose a verification method and enter the code."
submitLabel = "Verify"
recoveryHref = "/recover"
signInHref = "/login"
color = "primary"
size = "md"
class = ""
}
state methodOptions = [
{"label":"Authenticator app","value":"totp"},
{"label":"Recovery code","value":"recovery-code"},
{"label":"Email code","value":"email-otp"},
{"label":"SMS code","value":"sms-otp"}
]
view {
<section
{...attrs}
data-wrnexus-runtime="auth"
data-auth-mfa-challenge
data-mfa-otp-action='{otpAction}'
data-mfa-initial-method='{method}'
class='w-full max-w-md rounded-[var(--wrn-radius-lg)] border border-[var(--wrn-color-border)] bg-[var(--wrn-color-surface)] p-6 text-[var(--wrn-color-text)] shadow-[var(--wrn-shadow-2)] {class}'
>
<div class="mb-5 flex size-11 items-center justify-center rounded-full bg-[color-mix(in_srgb,var(--wrn-color-primary)_12%,transparent)] text-[var(--wrn-color-primary)]"><span class="icon-[lucide--shield-check] size-5"></span></div>
<h1 class="m-0 text-2xl font-semibold">{title}</h1>
<p class="mt-2 text-sm text-[var(--wrn-color-muted)]">{description}</p>
<form method="post" action='{action}' data-schema='{schema}' data-auth-mfa-form novalidate class="mt-6 space-y-4">
<input type="hidden" name="mfaToken" value='{mfaToken}' />
<input type="hidden" name="challengeId" value='{challengeId}' />
<input type="hidden" name="returnTo" value='{returnTo}' />
<input type="hidden" name="code" value="" />
<Radio id="auth-mfa-method" name="method" data-auth-mfa-method label="Verification method" value="{method}" options="{methodOptions}" card="true" color="{color}" size="{size}" />
<div data-auth-mfa-pin><PinInput label="Verification code" name="otpCode" length="6" inputMode="numeric" autocomplete="one-time-code" color="{color}" size="{size}" /></div>
<div data-auth-mfa-recovery hidden><Input label="Recovery code" name="recoveryCode" autocomplete="off" maxlength="32" color="{color}" size="{size}" /></div>
<p data-error="code" class="m-0 text-sm text-[var(--wrn-color-danger)]" aria-live="polite"></p>
<Checkbox name="rememberDevice" label="Trust this device for 30 days" value="true" />
<p data-auth-mfa-status role="status" hidden class="m-0 rounded-[var(--wrn-radius-sm)] bg-[color-mix(in_srgb,var(--wrn-color-primary)_10%,transparent)] p-3 text-sm text-[var(--wrn-color-primary)]"></p>
<p data-error="mfaToken" class="m-0 min-h-4 text-xs text-[var(--wrn-color-danger)]"></p>
<p data-error="challengeId" class="m-0 min-h-4 text-xs text-[var(--wrn-color-danger)]"></p>
<p data-error="_form" role="alert" class="m-0 hidden rounded-[var(--wrn-radius-sm)] bg-[color-mix(in_srgb,var(--wrn-color-danger)_10%,transparent)] p-3 text-sm text-[var(--wrn-color-danger)]"></p>
<Button type="submit" label="{submitLabel}" variant="solid" color="{color}" size="{size}" fullWidth="true" />
</form>
<p data-auth-mfa-expired role="alert" hidden class="wrn-auth-mfa-expired">Your sign-in request has expired. Sign in again to get a fresh verification request.</p>
<nav class="wrn-auth-mfa-links" aria-label="Sign-in help">
<a href='{signInHref}' data-auth-mfa-restart hidden>Back to sign in</a>
<a href='{recoveryHref}'>Account recovery options</a>
</nav>
</section>
}
style {
[data-auth-mfa-challenge][data-mfa-expired="true"] [data-auth-mfa-form] { display: none; }
.wrn-auth-mfa-expired { margin: 1.5rem 0 0; padding: 1rem; border: 1px solid var(--wrn-color-danger); border-radius: var(--wrn-radius-sm); background: color-mix(in srgb, var(--wrn-color-danger) 10%, transparent); color: var(--wrn-color-danger); font-size: 0.875rem; line-height: 1.5; }
.wrn-auth-mfa-links { display: flex; flex-direction: column; align-items: flex-start; gap: 0.75rem; margin-top: 1.25rem; }
.wrn-auth-mfa-links a { color: var(--wrn-color-primary); font-size: 0.875rem; }
.wrn-auth-mfa-links a:hover { text-decoration: underline; }
}
}