isLoggedIn() && $auth->isAdmin(); if ($auth->isLoggedIn() && !$isPreview) { header('Location: index.php'); exit; } } catch (Exception $e) { die('Fehler beim Initialisieren: ' . $e->getMessage()); } I18n::init(); $error = ''; $success = ''; $show2fa = false; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['totp_code'])) { // Zweiter Login-Schritt: 2FA-Code try { if ($auth->verifyTotpLogin(trim($_POST['totp_code']))) { header('Location: index.php'); exit; } $error = 'Code ungültig oder abgelaufen. Bitte erneut versuchen.'; $show2fa = $auth->isTotpPending(); } catch (Exception $e) { $error = 'Login-Fehler: ' . $e->getMessage(); } } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { try { $email = trim($_POST['email'] ?? ''); $password = $_POST['password'] ?? ''; if (empty($email) || empty($password)) { $error = __('login_error_empty'); } else { $result = $auth->login($email, $password); if ($result === true) { header('Location: index.php'); exit; } elseif ($result === 'totp_required') { $show2fa = true; } elseif ($result === 'rate_limited') { $error = __('login_error_rate'); } else { $error = __('login_error_creds'); } } } catch (Exception $e) { $error = 'Login-Fehler: ' . $e->getMessage(); } } // Direkter Aufruf mit ?2fa=1 (z.B. nach Redirect) und noch ausstehendem Login if (!$show2fa && isset($_GET['2fa']) && $auth->isTotpPending()) { $show2fa = true; } try { $db = Database::getInstance(); $appTitle = $db->getSetting('app_title', 'UniFi Voucher System'); $logoUrl = $db->getSetting('logo_url', ''); $m365ClientId = $db->getSetting('m365_client_id', ''); $m365ClientSecret = $db->getSetting('m365_client_secret', ''); $m365TenantId = $db->getSetting('m365_tenant_id', ''); $m365Enabled = !empty($m365ClientId) && !empty($m365ClientSecret) && !empty($m365TenantId); $publicAccess = $db->getSetting('public_access', 0); $smtpEnabled = $db->getSetting('smtp_enabled', '0') === '1'; $m365LoginUrl = ''; if ($m365Enabled) { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $scriptPath = dirname($_SERVER['SCRIPT_NAME']); $scriptPath = $scriptPath === '/' ? '' : $scriptPath; $redirectUri = $protocol . '://' . $host . $scriptPath . '/m365_callback.php'; $params = [ 'client_id' => $m365ClientId, 'response_type' => 'code', 'redirect_uri' => $redirectUri, 'response_mode' => 'query', 'scope' => 'openid profile email User.Read', 'state' => bin2hex(random_bytes(16)) ]; $_SESSION['m365_state'] = $params['state']; $m365LoginUrl = "https://login.microsoftonline.com/$m365TenantId/oauth2/v2.0/authorize?" . http_build_query($params); } // Generisches OIDC (optional) $oidcEnabled = $db->getSetting('oidc_enabled', '0') === '1' && $db->getSetting('oidc_client_id', '') !== '' && $db->getSetting('oidc_auth_url', '') !== ''; $oidcName = $db->getSetting('oidc_name', 'SSO'); $oidcLoginUrl = ''; if ($oidcEnabled) { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; $scriptPath = dirname($_SERVER['SCRIPT_NAME']); $scriptPath = $scriptPath === '/' ? '' : $scriptPath; $oidcState = bin2hex(random_bytes(16)); $_SESSION['oidc_state'] = $oidcState; $oidcLoginUrl = rtrim($db->getSetting('oidc_auth_url', ''), '?') . '?' . http_build_query([ 'client_id' => $db->getSetting('oidc_client_id', ''), 'response_type' => 'code', 'redirect_uri' => $protocol . '://' . $_SERVER['HTTP_HOST'] . $scriptPath . '/oidc_callback.php', 'scope' => $db->getSetting('oidc_scopes', 'openid profile email'), 'state' => $oidcState, ]); } $showLocalLogin = isset($_GET['local']) && $_GET['local'] === '1'; // --- Individualisierung der Login-Seite ------------------------------- // Alle Werte sind optional; leer bedeutet "Standard verwenden". $loginBrand = $db->getSetting('login_brand_name', '') ?: $appTitle; $loginLogo = $db->getSetting('login_logo_url', '') ?: $logoUrl; $showPanel = $db->getSetting('login_panel_enabled', '1') === '1'; $claimTitle = $db->getSetting('login_claim_title', '') ?: __('auth_claim_title'); $claimText = $db->getSetting('login_claim_text', '') ?: __('auth_claim_text'); $featureRaw = trim((string)$db->getSetting('login_features', '')); if ($featureRaw !== '') { $loginFeatures = array_values(array_filter(array_map('trim', preg_split('/\r\n|\r|\n/', $featureRaw)))); } else { $loginFeatures = [__('auth_feature_1'), __('auth_feature_2'), __('auth_feature_3')]; } $loginFooter = $db->getSetting('login_footer', '') ?: ('© ' . date('Y') . ' ' . $loginBrand); $loginBgImage = trim((string)$db->getSetting('login_bg_image', '')); $loginBgFrom = $db->getSetting('login_bg_from', '') ?: '#3b2f8f'; $loginBgTo = $db->getSetting('login_bg_to', '') ?: '#6d5ce7'; $loginOverlay = max(0, min(90, (int)$db->getSetting('login_bg_overlay', '40'))); $visualStyle = '--login-from:' . htmlspecialchars($loginBgFrom, ENT_QUOTES) . ';--login-to:' . htmlspecialchars($loginBgTo, ENT_QUOTES) . ';--login-overlay:' . ($loginOverlay / 100); if ($loginBgImage !== '') { $visualStyle .= ";--login-image:url('" . htmlspecialchars($loginBgImage, ENT_QUOTES) . "')"; } } catch (Exception $e) { die('Datenbankfehler: ' . $e->getMessage()); } ?> <?= __('login_title') ?> – <?= htmlspecialchars($appTitle) ?>

$label): ?>

Bitte geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein – oder einen Ihrer Recovery-Codes.