Login mit E-Mail-Code (Appwrite-2FA)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
21325c4037
commit
b8e4de97c7
5 files changed
+253
-7
No files matched your search
+103
-4
@@ -8,8 +8,10 @@ export default function LoginPage() {
|
||||
const [password, setPassword] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const { login } = useAuth()
|
||||
const [code, setCode] = useState('')
|
||||
const [notice, setNotice] = useState('')
|
||||
|
||||
const { login, mfaPending, sendCode, verifyCode, cancelMfa } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
@@ -22,6 +24,8 @@ export default function LoginPage() {
|
||||
|
||||
if (result.success) {
|
||||
navigate('/tickets')
|
||||
} else if (result.mfaRequired) {
|
||||
if (result.error) setError(result.error)
|
||||
} else {
|
||||
// Bessere Fehlermeldungen
|
||||
let errorMessage = result.error || 'Login fehlgeschlagen'
|
||||
@@ -41,6 +45,101 @@ export default function LoginPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleCode = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
setLoading(true)
|
||||
const result = await verifyCode(code)
|
||||
setLoading(false)
|
||||
if (result.success) {
|
||||
navigate('/tickets')
|
||||
} else {
|
||||
setError(result.error)
|
||||
setCode('')
|
||||
}
|
||||
}
|
||||
|
||||
const handleSend = async () => {
|
||||
setError('')
|
||||
setNotice('')
|
||||
setLoading(true)
|
||||
const result = await sendCode()
|
||||
setLoading(false)
|
||||
if (result.success) setNotice('Neuer Code ist unterwegs.')
|
||||
else setError(result.error)
|
||||
}
|
||||
|
||||
const handleCancel = async () => {
|
||||
await cancelMfa()
|
||||
setError('')
|
||||
setNotice('')
|
||||
setCode('')
|
||||
setPassword('')
|
||||
}
|
||||
|
||||
const linkButton = {
|
||||
background: 'none',
|
||||
border: 0,
|
||||
padding: 0,
|
||||
color: '#e2e8f0',
|
||||
textDecoration: 'underline',
|
||||
cursor: 'pointer',
|
||||
font: 'inherit',
|
||||
}
|
||||
|
||||
const codeStep = mfaPending && (
|
||||
<form onSubmit={handleCode}>
|
||||
{error && (
|
||||
<div className="bg-red text-white p-1 mb-2" style={{ borderRadius: '4px' }}>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<p style={{ marginTop: 0 }}>
|
||||
{!mfaPending.challengeId
|
||||
? 'Wir schicken dir einen Code an deine E-Mail-Adresse.'
|
||||
: `Wir haben dir einen Code an ${mfaPending.email || 'deine E-Mail-Adresse'} geschickt.`}
|
||||
</p>
|
||||
{notice && <p style={{ marginTop: 0 }}>{notice}</p>}
|
||||
{mfaPending.challengeId ? (
|
||||
<>
|
||||
<div className="form-group">
|
||||
<label className="form-label" htmlFor="mfa-code">Code aus der E-Mail</label>
|
||||
<input
|
||||
id="mfa-code"
|
||||
type="text"
|
||||
className="form-control"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value.replace(/\D/g, ''))}
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
maxLength={6}
|
||||
required
|
||||
autoFocus
|
||||
style={{ letterSpacing: '0.3em' }}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-green" style={{ width: '100%' }} disabled={loading}>
|
||||
{loading ? 'Prüfe…' : 'Anmelden'}
|
||||
</button>
|
||||
<p style={{ textAlign: 'center', marginBottom: 0 }}>
|
||||
<button type="button" style={linkButton} onClick={handleSend} disabled={loading}>Code erneut senden</button>
|
||||
{' · '}
|
||||
<button type="button" style={linkButton} onClick={handleCancel}>Abbrechen</button>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button type="button" className="btn btn-green" style={{ width: '100%' }} onClick={handleSend} disabled={loading}>
|
||||
{loading ? 'Sende…' : 'Code senden'}
|
||||
</button>
|
||||
<p style={{ textAlign: 'center', marginBottom: 0 }}>
|
||||
<button type="button" style={linkButton} onClick={handleCancel}>Abbrechen</button>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'relative',
|
||||
@@ -95,7 +194,7 @@ export default function LoginPage() {
|
||||
<h2>Webklar WOMS 2.0</h2>
|
||||
</div>
|
||||
<div style={{ padding: '16px', color: '#e2e8f0' }}>
|
||||
<form onSubmit={handleSubmit}>
|
||||
{codeStep || <form onSubmit={handleSubmit}>
|
||||
{error && (
|
||||
<div className="bg-red text-white p-1 mb-2" style={{ borderRadius: '4px' }}>
|
||||
{error}
|
||||
@@ -135,7 +234,7 @@ export default function LoginPage() {
|
||||
>
|
||||
{loading ? 'Login läuft...' : 'Login'}
|
||||
</button>
|
||||
</form>
|
||||
</form>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user