login_2fa.html 80 lignes · 3922 octets
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ page_title }}</title>
    <link href="/static/css/tailwind.css" rel="stylesheet">
    <link href="/static/css/custom.css" rel="stylesheet">
    <script src="/static/js/alpine-components.js" defer></script>
    <script src="/static/js/alpine-csp.min.js" defer></script>
</head>
<body class="min-h-screen bg-base-200">
    <div class="min-h-screen flex items-center justify-center px-4" x-data="twoFactorPage" data-error-invalid="{{ error_invalid_code }}" data-error-network="{{ error_network }}">
        <div class="card bg-base-100 shadow-xl w-full max-w-md">
            <div class="card-body">
                <div class="text-center mb-6">
                    <svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 mx-auto text-primary mb-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
                    </svg>
                    <h1 class="text-2xl font-bold">{{ title }}</h1>
                    <p class="text-base-content/70 mt-1">{{ subtitle }}</p>
                </div>

                <!-- TOTP Code Input -->
                <div x-show="!useBackupCode">
                    <label class="label"><span class="label-text font-semibold">{{ code_label }}</span></label>
                    <input
                        type="text"
                        x-model="code"
                        maxlength="6"
                        pattern="[0-9]*"
                        inputmode="numeric"
                        autocomplete="one-time-code"
                        class="input input-bordered w-full text-center text-2xl tracking-widest"
                        placeholder="000000"
                        @keyup.enter="verify()"
                        autofocus
                    />
                </div>

                <!-- Backup Code Input -->
                <div x-show="useBackupCode" x-cloak>
                    <label class="label"><span class="label-text font-semibold">{{ backup_code_label }}</span></label>
                    <input
                        type="text"
                        x-model="code"
                        maxlength="8"
                        class="input input-bordered w-full text-center text-xl tracking-widest font-mono"
                        placeholder="ABCD1234"
                        @keyup.enter="verify()"
                    />
                </div>

                <!-- Error message -->
                <p x-show="error" class="text-error text-sm mt-2" x-text="error"></p>

                <!-- Submit button -->
                <button class="btn btn-primary w-full mt-4" @click="verify()" :disabled="!code || verifying">
                    <span x-show="!verifying">{{ verify_button }}</span>
                    <span x-show="verifying" class="loading loading-spinner loading-sm"></span>
                </button>

                <!-- Toggle backup code -->
                <div class="text-center mt-4">
                    <button class="btn btn-ghost btn-sm" @click="toggleBackup()">
                        <span x-show="!useBackupCode">{{ use_backup_code }}</span>
                        <span x-show="useBackupCode">{{ use_totp_code }}</span>
                    </button>
                </div>

                <!-- Back to login -->
                <div class="text-center mt-2">
                    <a href="/login" class="link link-hover text-sm">{{ back_to_login }}</a>
                </div>
            </div>
        </div>
    </div>

</body>
</html>