/** * STAIRCASE LOGIN HANDLER * * Handles all login methods: * - Passkey (WebAuthn) * - Access Code * - Password (traditional) * * @file sc_login.js * @version 2.0.0 (Passkey-ready) * @date 2025-01-08 */ $(document).ready(function() { // ========================================================================= // INITIALIZATION // ========================================================================= // Passkey-Button verstecken wenn Browser WebAuthn NICHT unterstützt if (!ScWebAuthn.isSupported()) { $('#passkey-login-btn').hide(); // Auch Divider verstecken wenn kein Passkey verfügbar $('#passkey-login-btn').prev('.login-divider').hide(); } // Passkey Login Button Handler $('#passkey-login-btn').on('click', function(e) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); handlePasskeyLogin(); return false; }); // Form Submit Handler $('#form_login').on('submit', function(e) { e.preventDefault(); handleFormSubmit(); }); // Hide error on input $('#username, #password').on('input', function() { hideLoginError(); }); // Clear passkey assertion when user starts typing password $('#password').on('input', function() { if ($(this).val().length > 0) { $('#passkey_assertion').val(''); } }); // ========================================================================= // PASSKEY LOGIN // ========================================================================= function handlePasskeyLogin() { showLoginProgress('Passkey wird verifiziert...'); ScWebAuthn.authenticate( function(assertionData) { // Success - send to server $('#passkey_assertion').val(JSON.stringify(assertionData)); submitLoginForm(); }, function(error) { // Error - Clear passkey assertion field! $('#passkey_assertion').val(''); hideLoginProgress(); showLoginError(error, 'error'); } ); } // ========================================================================= // FORM SUBMIT // ========================================================================= function handleFormSubmit() { const username = $('#username').val().trim(); const password = $('#password').val(); const passkeyAssertion = $('#passkey_assertion').val(); // Username IMMER erforderlich if (!username) { showLoginError('Bitte geben Sie Ihren Benutzernamen ein', 'warning'); $('#username').focus(); return false; } // Mindestens eine Auth-Methode: Password ODER Passkey if (!password && !passkeyAssertion) { showLoginError('Bitte geben Sie Ihr Passwort ein oder melden Sie sich mit Passkey an', 'warning'); $('#password').focus(); return false; } showLoginProgress('Anmeldung wird verarbeitet...'); submitLoginForm(); } function submitLoginForm() { const passkeyAssertion = $('#passkey_assertion').val(); // Build data object explicitly to ensure all required fields are present const dataToSend = { username: $('#username').val(), csrf_token: $('#csrf_token').val(), X1: $('#X1').val(), redURL: $('#redURL').val() }; // Add authentication method if (passkeyAssertion && passkeyAssertion !== '') { // Passkey login - send assertion instead of password dataToSend.passkey_assertion = passkeyAssertion; } else { // Normal password login dataToSend.password = $('#password').val(); } $.ajax({ url: 'ajax.sc?type=login', type: 'POST', data: dataToSend, dataType: 'json', success: function(response, textStatus, xhr) { // JSON Response behandeln // sc_rights gibt login_status zurück: "success" = OK, alles andere = Error if (response.login_status === 'success') { // Erfolgreich - pageloader läuft bereits, einfach redirect const redirectUrl = response.redirect_url || $('#redURL').val() || '/'; window.location.href = redirectUrl; } else { // Login fehlgeschlagen - zeige Fehlermeldung hideLoginProgress(); const errorMsg = response.login_message || response.loginmsg || 'Anmeldung fehlgeschlagen'; const errorTitle = response.login_message_title || response.logintitle || 'Fehler beim Einloggen'; showLoginError(errorMsg, 'error', errorTitle); } }, error: function(xhr, textStatus, errorThrown) { hideLoginProgress(); console.error('AJAX Error:', { status: xhr.status, statusText: xhr.statusText, responseText: xhr.responseText, textStatus: textStatus, errorThrown: errorThrown }); // Spezialfall: JSON-Parse-Error if (textStatus === 'parsererror') { console.error('JSON Parse Error. Response:', xhr.responseText); showLoginError( 'Server hat ungültige Antwort gesendet. Bitte kontaktieren Sie den Support.', 'error', 'Konfigurationsfehler' ); return; } // Versuche JSON aus Error-Response zu parsen try { const response = JSON.parse(xhr.responseText); showLoginError( response.loginmsg || response.message || 'Anmeldung fehlgeschlagen', 'error', response.logintitle || 'Fehler' ); } catch (e) { // Kein JSON - zeige generische Fehlermeldung basierend auf HTTP-Status let errorMessage = 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'; let errorTitle = 'Fehler beim Einloggen'; if (xhr.status === 0) { errorMessage = 'Keine Verbindung zum Server. Bitte prüfen Sie Ihre Internetverbindung.'; errorTitle = 'Verbindungsfehler'; } else if (xhr.status === 404) { errorMessage = 'Login-Endpunkt nicht gefunden (404). Bitte kontaktieren Sie den Support.'; errorTitle = 'Seite nicht gefunden'; } else if (xhr.status === 500) { errorMessage = 'Server-Fehler (500). Bitte versuchen Sie es später erneut.'; errorTitle = 'Server-Fehler'; } else if (xhr.status === 503) { errorMessage = 'Server vorübergehend nicht verfügbar (503). Bitte versuchen Sie es später erneut.'; errorTitle = 'Service nicht verfügbar'; } else if (xhr.status >= 400 && xhr.status < 500) { errorMessage = 'Client-Fehler (' + xhr.status + '). Bitte laden Sie die Seite neu.'; errorTitle = 'Anfragefehler'; } else if (xhr.status >= 500) { errorMessage = 'Server-Fehler (' + xhr.status + '). Bitte kontaktieren Sie den Support.'; errorTitle = 'Server-Fehler'; } else if (textStatus === 'timeout') { errorMessage = 'Zeitüberschreitung. Bitte versuchen Sie es erneut.'; errorTitle = 'Timeout'; } else if (textStatus === 'abort') { errorMessage = 'Anfrage abgebrochen. Bitte versuchen Sie es erneut.'; errorTitle = 'Abgebrochen'; } showLoginError(errorMessage, 'error', errorTitle); } } }); } // ========================================================================= // UI HELPERS // ========================================================================= function showLoginProgress(message) { // Pageloader sofort einblenden (kein Fade) — verhindert doppeltes Logo während Crossfade var $pl = $('#sc-pageloader'); $pl.css({ transition: 'none', opacity: 1, 'pointer-events': 'auto' }); $('.login-submit').prop('disabled', true); $('#passkey-login-btn').prop('disabled', true); } function hideLoginProgress() { // Pageloader ausblenden (z.B. bei Login-Fehler) var $pl = $('#sc-pageloader'); $pl.css({ transition: 'opacity 0.25s ease', opacity: 0, 'pointer-events': 'none' }); $('.login-submit').prop('disabled', false); $('#passkey-login-btn').prop('disabled', false); } function showLoginError(message, type, title) { const $error = $('.sc-login-error'); // Set title if (title) { $('.sc-login-error-title').text(title); } else { $('.sc-login-error-title').text(type === 'warning' ? 'Achtung' : 'Fehler beim Einloggen'); } // Set message $('.sc-login-error-message').html(message); // Set alert class $error.removeClass('alert-danger alert-warning alert-info'); if (type === 'warning') { $error.addClass('alert-warning'); } else if (type === 'info') { $error.addClass('alert-info'); } else { $error.addClass('alert-danger'); } // Show error $error.slideDown(300); // Auto-hide after 10 seconds (except errors) if (type !== 'error') { setTimeout(function() { $error.slideUp(300); }, 10000); } } function hideLoginError() { $('.sc-login-error').slideUp(300); } });