Portal: Mitarbeiter-Login gesperrt (Hinweis aufs Ticketsystem), alte Mitarbeiter-Sitzungen enden
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d0ca3c41cd
commit
3a4fa581c0
13 files changed
+145
-559
No files matched your search
+14
-75
@@ -17,6 +17,8 @@ async function api(path, options = {}) {
|
||||
err.status = response.status
|
||||
if (data.code) err.code = data.code
|
||||
if (data.retryAfterSeconds) err.retryAfterSeconds = data.retryAfterSeconds
|
||||
// Mitarbeiter-Login gesperrt: Adresse des Ticketsystems fuer den Link unter dem Hinweis
|
||||
if (data.ticketUrl) err.ticketUrl = data.ticketUrl
|
||||
// Passwort auf einem anderen Gerät geändert: diese Sitzung gilt nicht mehr
|
||||
if (data.code === 'session_revoked' && document.body.classList.contains('dashboard-page') && !sessionEndRedirected) {
|
||||
sessionEndRedirected = true
|
||||
@@ -473,17 +475,12 @@ async function postLoginRedirect() {
|
||||
window.location.href = await resolvePostLoginTarget()
|
||||
}
|
||||
|
||||
// Links aus Admin-Mails (z. B. /admin.html?gutschrift=<id>) nach dem Login wieder aufnehmen
|
||||
function adminLoginTarget() {
|
||||
const next = getQueryParam('next')
|
||||
return /^\/admin\.html(\?[A-Za-z0-9=&_-]*)?$/.test(next) ? next : '/admin.html'
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- Login-Page --- */
|
||||
async function initLoginPage() {
|
||||
const form = document.getElementById('login-form')
|
||||
const errorEl = document.getElementById('login-error')
|
||||
const btn = document.getElementById('login-btn')
|
||||
const staffLink = document.getElementById('login-staff-link')
|
||||
|
||||
const errorFromQuery = getQueryParam('error')
|
||||
if (errorFromQuery) {
|
||||
@@ -523,11 +520,8 @@ async function initLoginPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
// Alte Mitarbeiter-Sitzung beendet /me selbst (reason staff): dann bleibt die Anmeldeseite stehen
|
||||
const me = await api('/api/auth/me')
|
||||
if (me.authenticated && me.role === 'admin' && !me.viewAs) {
|
||||
window.location.href = adminLoginTarget()
|
||||
return
|
||||
}
|
||||
if (me.authenticated && me.customer) {
|
||||
await postLoginRedirect()
|
||||
return
|
||||
@@ -540,6 +534,7 @@ async function initLoginPage() {
|
||||
e.preventDefault()
|
||||
if (btn.disabled) return
|
||||
errorEl.classList.add('hidden')
|
||||
staffLink?.classList.add('hidden')
|
||||
btn.disabled = true
|
||||
btn.classList.add('is-loading')
|
||||
btn.setAttribute('aria-busy', 'true')
|
||||
@@ -552,18 +547,14 @@ async function initLoginPage() {
|
||||
password: document.getElementById('password').value,
|
||||
}),
|
||||
})
|
||||
if (result.mfaRequired) {
|
||||
showMfaStep(result.email)
|
||||
return
|
||||
}
|
||||
if (result.role === 'admin') {
|
||||
window.location.href = adminLoginTarget()
|
||||
return
|
||||
}
|
||||
await postLoginRedirect()
|
||||
return
|
||||
} catch (err) {
|
||||
showError(errorEl, err.message)
|
||||
if (err.ticketUrl && staffLink) {
|
||||
staffLink.href = err.ticketUrl
|
||||
staffLink.classList.remove('hidden')
|
||||
}
|
||||
if (err.status === 429) {
|
||||
cooldownActive = true
|
||||
await applyCooldown(err.retryAfterSeconds || 900)
|
||||
@@ -576,59 +567,6 @@ async function initLoginPage() {
|
||||
})
|
||||
}
|
||||
|
||||
/* Mitarbeiter-2FA: Code aus der E-Mail (POST /api/auth/login/mfa) */
|
||||
function showMfaStep(maskedEmail) {
|
||||
const loginForm = document.getElementById('login-form')
|
||||
const form = document.getElementById('mfa-form')
|
||||
const input = document.getElementById('mfa-code')
|
||||
const errorEl = document.getElementById('mfa-error')
|
||||
const btn = document.getElementById('mfa-btn')
|
||||
const resend = document.getElementById('mfa-resend')
|
||||
loginForm?.classList.add('hidden')
|
||||
form?.classList.remove('hidden')
|
||||
document.getElementById('mfa-hint').textContent = `Wir haben dir einen Code an ${maskedEmail} geschickt.`
|
||||
input?.focus()
|
||||
|
||||
form?.addEventListener('submit', async (e) => {
|
||||
e.preventDefault()
|
||||
if (btn.disabled) return
|
||||
hideError(errorEl)
|
||||
btn.disabled = true
|
||||
btn.classList.add('is-loading')
|
||||
try {
|
||||
await api('/api/auth/login/mfa', { method: 'POST', body: JSON.stringify({ otp: input.value.trim() }) })
|
||||
window.location.href = adminLoginTarget()
|
||||
} catch (err) {
|
||||
if (err.status === 410) {
|
||||
window.location.href = `/login.html?error=${encodeURIComponent(err.message)}`
|
||||
return
|
||||
}
|
||||
showError(errorEl, err.message)
|
||||
input.select()
|
||||
} finally {
|
||||
btn.disabled = false
|
||||
btn.classList.remove('is-loading')
|
||||
}
|
||||
})
|
||||
|
||||
resend?.addEventListener('click', async () => {
|
||||
hideError(errorEl)
|
||||
resend.disabled = true
|
||||
try {
|
||||
await api('/api/auth/login/mfa/resend', { method: 'POST' })
|
||||
document.getElementById('mfa-hint').textContent = `Neuer Code ist unterwegs an ${maskedEmail}.`
|
||||
} catch (err) {
|
||||
if (err.status === 410) {
|
||||
window.location.href = `/login.html?error=${encodeURIComponent(err.message)}`
|
||||
return
|
||||
}
|
||||
showError(errorEl, err.message)
|
||||
} finally {
|
||||
resend.disabled = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- View-as --- */
|
||||
function renderViewAsBanner(me) {
|
||||
const banner = document.getElementById('viewas-banner')
|
||||
@@ -2088,16 +2026,17 @@ async function initDashboardPage() {
|
||||
const me = await api('/api/auth/me')
|
||||
|
||||
if (!me.authenticated) {
|
||||
// Alte Mitarbeiter-Sitzung (Mitarbeiter arbeiten im Ticketsystem): dorthin weiter
|
||||
if (me.reason === 'staff' && me.redirect) {
|
||||
window.location.href = me.redirect
|
||||
return
|
||||
}
|
||||
const target = new URL(loginUrl, window.location.origin)
|
||||
// Passwort auf einem anderen Gerät geändert oder Kundenansicht abgelaufen: Hinweis auf der Anmeldeseite
|
||||
if ((me.reason === 'password_changed' || me.reason === 'view_expired') && me.message) target.searchParams.set('error', me.message)
|
||||
window.location.href = target.pathname + target.search
|
||||
return
|
||||
}
|
||||
if (me.role === 'admin' && !me.customer) {
|
||||
window.location.href = '/admin.html'
|
||||
return
|
||||
}
|
||||
if (!me.customer) {
|
||||
window.location.href = loginUrl
|
||||
return
|
||||
|
||||
+8
-13
@@ -655,17 +655,12 @@ a.portal-btn-outline {
|
||||
}
|
||||
}
|
||||
|
||||
/* Mitarbeiter-2FA: Code-Schritt */
|
||||
#mfa-form.hidden { display: none; }
|
||||
.mfa-hint { margin: 0 0 4px; text-align: center; }
|
||||
#mfa-code { letter-spacing: 0.3em; font-variant-numeric: tabular-nums; }
|
||||
.mfa-actions { margin-top: 12px; }
|
||||
.mfa-link {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
/* Mitarbeiter-Login gesperrt: Link ins Ticketsystem unter dem Hinweis */
|
||||
.login-staff-link {
|
||||
display: block;
|
||||
margin: 8px 0 0;
|
||||
text-align: center;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
+2
-25
@@ -62,6 +62,8 @@
|
||||
enterkeyhint="go"
|
||||
>
|
||||
<p id="login-error" class="error hidden"></p>
|
||||
<!-- Mitarbeiter-Login ist gesperrt: Link ins Ticketsystem (Adresse aus der Antwort, ticketUrl) -->
|
||||
<a id="login-staff-link" class="login-staff-link hidden" href="https://ticket.webklar.com">Zum Ticketsystem</a>
|
||||
<button type="submit" id="login-btn" class="auth-button login-button">
|
||||
<span class="button-label">Anmelden</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 11 10" aria-hidden="true">
|
||||
@@ -72,31 +74,6 @@
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
<!-- Mitarbeiter mit 2FA: zweiter Schritt, Code aus der E-Mail -->
|
||||
<form id="mfa-form" class="login-form hidden" novalidate>
|
||||
<p id="mfa-hint" class="login-hint mfa-hint"></p>
|
||||
<label class="auth-label" for="mfa-code">Code aus der E-Mail</label>
|
||||
<input
|
||||
class="auth-input"
|
||||
type="text"
|
||||
name="otp"
|
||||
id="mfa-code"
|
||||
inputmode="numeric"
|
||||
autocomplete="one-time-code"
|
||||
maxlength="6"
|
||||
pattern="[0-9]{6}"
|
||||
enterkeyhint="go"
|
||||
>
|
||||
<p id="mfa-error" class="error hidden"></p>
|
||||
<button type="submit" id="mfa-btn" class="auth-button login-button">
|
||||
<span class="button-label">Anmelden</span>
|
||||
</button>
|
||||
<p class="login-hint mfa-actions">
|
||||
<button type="button" id="mfa-resend" class="mfa-link">Code erneut senden</button>
|
||||
·
|
||||
<a href="/login.html" class="mfa-link">Abbrechen</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<p class="login-hint">
|
||||
Zugangsdaten erhältst du von Webklar. Dein Passwort kannst du nach der Anmeldung selbst ändern (Profilbild oben rechts, „Passwort ändern“).
|
||||
|
||||
Reference in new issue
Block a user