From e639431609da1f94d2685dc6e27eb1802366865d Mon Sep 17 00:00:00 2001 From: Wajahat Ali Date: Wed, 16 Sep 2026 12:19:55 +0500 Subject: [PATCH 1/2] feat: support 2fa login step via url parameters and update totp input ui --- web/src/pages/Login.css | 25 ++++++++++++++++--------- web/src/pages/Login.tsx | 21 ++++++++++++--------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/web/src/pages/Login.css b/web/src/pages/Login.css index 201a8b0..4925622 100644 --- a/web/src/pages/Login.css +++ b/web/src/pages/Login.css @@ -206,13 +206,14 @@ } .login-input-2fa input.input-totp { width:100%; - height:52px; - padding:0 16px; + height:56px; + padding:2px 16px 12px; text-align:center; font-family:var(--mono); font-size:24px; font-weight:750; letter-spacing:.32em; + text-indent:.32em; color:#5eead4; background:#080c11; border:1.5px solid rgba(45,212,191,.35); @@ -273,25 +274,31 @@ .totp-indicator-row { display:flex; align-items:center; - justify-content:space-between; + justify-content:center; margin-top:8px; padding:0 4px; } .totp-slots { + position:absolute; + bottom:9px; + left:50%; + transform:translateX(-50%); display:flex; align-items:center; - gap:6px; + gap:10px; + pointer-events:none; + z-index:2; } .totp-slot { - width:9px; - height:4px; + width:12px; + height:3px; border-radius:999px; - background:rgba(56,68,86,.6); + background:rgba(56,68,86,.65); transition:all .2s cubic-bezier(.16,1,.3,1); } .totp-slot.active { - background:rgba(94,234,212,.4); - transform:scaleX(1.2); + background:rgba(94,234,212,.5); + box-shadow:0 0 6px rgba(94,234,212,.4); } .totp-slot.filled { background:#2dd4bf; diff --git a/web/src/pages/Login.tsx b/web/src/pages/Login.tsx index 1fc5419..c3bcefd 100644 --- a/web/src/pages/Login.tsx +++ b/web/src/pages/Login.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { useNavigate, useLocation } from 'react-router-dom' +import { useNavigate, useLocation, useSearchParams } from 'react-router-dom' import { api } from '../api' import './Login.css' @@ -42,7 +42,10 @@ export default function Login() { const [serverHealthy, setServerHealthy] = useState(null) const [error, setError] = useState('') const [loading, setLoading] = useState(false) - const [step, setStep] = useState<'credentials' | '2fa'>('credentials') + const [searchParams] = useSearchParams() + const [step, setStep] = useState<'credentials' | '2fa'>( + searchParams.get('step') === '2fa' ? '2fa' : 'credentials' + ) const [preAuthToken, setPreAuthToken] = useState('') const [twoFactorCode, setTwoFactorCode] = useState('') const [useRecovery, setUseRecovery] = useState(false) @@ -332,10 +335,8 @@ export default function Login() { )} - - {!useRecovery && ( - + + {twoFactorEnabled && ( +
+ + + {deviceTrusted ? 'This browser is remembered (2FA code skipped for 30 days).' : 'This browser is not remembered (2FA code required every login).'} + +
+ )} + + {deviceMsg && ( +
+ + {deviceMsg} +
+ )}