Files
GoNtech/web/templates/stranice/totp_provera.html
T
Dasko b112d46e4e feat(2fa): rezervni (jednokratni) kodovi za 2FA
Alternativa TOTP-u kada uređaj nije dostupan. Po CLAUDE.md specifikaciji:
10 kodova pri aktivaciji, čuvani kao bcrypt heš.

Backend:
- migracija 039 (tabela rezervni_kodovi, FK CASCADE)
- auth.GenerisiRezervneKodove (Crockford base32, XXXX-XXXX) + NormalizujRezervniKod
- RezervniKodoviRepository (Zameni/Iskoristi/BrojPreostalih/Obrisi) + SQLite impl
- žičenje u Handler (+ reinicijalizuj)

Prijava:
- VerifikujTotp prvo proba TOTP, pa rezervni kod (isto polje); kod je jednokratni
- totp_provera.html: input opušten (slova/crtica), napomena o rezervnom kodu

Profil:
- aktivacija generiše i prikazuje kodove JEDNOM; dugme Regeneriši; brojač preostalo X/10
- deaktivacija briše kodove

Testovi: auth (generisanje/format/normalizacija), repo (jednokratnost/regeneracija),
prijava rezervnim kodom end-to-end. Ukupno 36 test funkcija.
2026-06-12 23:44:09 +02:00

53 lines
2.9 KiB
HTML

<!DOCTYPE html>
<html lang="sr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dvostepena verifikacija — NTech</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f1117; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 16px; }
.kartica { background: #1a1d27; border: 0.5px solid #2d3148; border-radius: 16px; padding: 40px; width: 100%; max-width: 380px; }
.logo { text-align: center; margin-bottom: 32px; }
.logo-naziv { font-size: 22px; font-weight: 600; color: #fff; }
.opis { font-size: 13px; color: #9ca3af; margin-bottom: 24px; line-height: 1.5; }
h1 { font-size: 18px; font-weight: 600; color: #fff; margin-bottom: 8px; }
.polje { margin-bottom: 16px; }
label { display: block; font-size: 13px; color: #9ca3af; margin-bottom: 6px; }
input { width: 100%; padding: 8px 12px; background: #0f1117; border: 0.5px solid #2d3148; border-radius: 8px; font-size: 20px; color: #fff; outline: none; text-align: center; letter-spacing: 6px; transition: border-color 0.2s; }
input:focus { border-color: #e53e3e; }
.dugme { width: 100%; padding: 11px; background: #e53e3e; color: #fff; border: none; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; margin-top: 8px; transition: opacity 0.2s; }
.dugme:hover { opacity: 0.88; }
.greska { background: #fef2f2; border: 0.5px solid #fca5a5; color: #dc2626; border-radius: 8px; padding: 10px 14px; font-size: 13px; margin-bottom: 20px; }
.nazad { display: block; text-align: center; margin-top: 16px; font-size: 13px; color: #6b7280; text-decoration: none; }
.nazad:hover { color: #9ca3af; }
</style>
</head>
<body>
<div class="kartica animiraj">
<div class="logo">
<div class="logo-naziv">NTech</div>
</div>
<h1>Dvostepena verifikacija</h1>
<p class="opis">Unesite 6-cifreni kod iz vaše aplikacije za autentifikaciju, ili jedan od rezervnih kodova (format XXXX-XXXX) ako nemate pristup aplikaciji.</p>
{{if eq .Greska "1"}}
<div class="greska">Neispravan kod. Pokušajte ponovo.</div>
{{end}}
<form method="POST" action="/prijava/totp">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<div class="polje">
<label for="kod">Kod za verifikaciju</label>
<input type="text" id="kod" name="kod"
maxlength="12" autocomplete="one-time-code" autofocus required>
</div>
<button type="submit" class="dugme">Potvrdi</button>
</form>
<a href="/odjava" class="nazad">← Nazad na prijavu</a>
</div>
</body>
</html>