50 lines
2.3 KiB
PHP
50 lines
2.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/common.php';
|
|
|
|
api_require_method('POST');
|
|
api_require_https();
|
|
$body = api_request_body();
|
|
$email = mb_strtolower(trim((string) ($body['email'] ?? '')));
|
|
$password = (string) ($body['password'] ?? '');
|
|
$totpCode = trim((string) ($body['totpCode'] ?? ''));
|
|
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL) || $password === '') {
|
|
api_error(422, 'CREDENTIALS_REQUIRED', 'E-Mail-Adresse und Passwort sind erforderlich.');
|
|
}
|
|
$ipOk = rate_limit_check('mobile_staff_login_ip', client_ip(), 10, 15);
|
|
$accountOk = !$ipOk || rate_limit_check('mobile_staff_login_email', strtolower($email), login_guard_max_attempts(), login_guard_lock_minutes());
|
|
if (!$ipOk || !$accountOk) {
|
|
if (!$accountOk) login_guard_locked($email, 'app'); // das IP-Limit allein sperrt kein Konto und löst keine Mail an den Kontoinhaber aus
|
|
api_error(429, 'RATE_LIMITED', 'Zu viele Anmeldeversuche. Bitte versuchen Sie es später erneut.');
|
|
}
|
|
|
|
$pdo = get_db();
|
|
$stmt = $pdo->prepare('SELECT * FROM staff WHERE LOWER(email) = LOWER(?) AND is_active = 1');
|
|
$stmt->execute([$email]);
|
|
$staff = $stmt->fetch();
|
|
if (!$staff || !password_verify($password, $staff['password_hash'])) {
|
|
login_guard_failed($staff ?: null, $email, 'app', $staff ? 'Falsches Passwort' : 'Unbekannte E-Mail-Adresse');
|
|
api_error(401, 'INVALID_CREDENTIALS', 'E-Mail-Adresse oder Passwort ist falsch.');
|
|
}
|
|
if (!empty($staff['totp_enabled'])) {
|
|
if ($totpCode === '') {
|
|
api_json(['requiresTotp' => true], 202);
|
|
}
|
|
if (!preg_match('/^\d{6}$/', $totpCode) || !totp_verify($staff['totp_secret'], $totpCode)) {
|
|
login_guard_failed($staff, $email, 'app', 'Falscher 2FA-Code');
|
|
api_error(401, 'INVALID_TOTP', 'Der Authenticator-Code ist ungültig.');
|
|
}
|
|
}
|
|
|
|
rate_limit_reset('mobile_staff_login_email', strtolower($email));
|
|
rate_limit_reset('mobile_staff_login_ip', client_ip());
|
|
$issued = api_issue_token($pdo, (int) $staff['id'], isset($body['deviceName']) ? (string) $body['deviceName'] : null);
|
|
audit_log((int) $staff['id'], 'Mobile App: Anmeldung');
|
|
login_guard_success($staff, 'app', isset($body['deviceName']) ? (string) $body['deviceName'] : null);
|
|
api_json([
|
|
'token' => $issued['token'],
|
|
'expiresAt' => $issued['expires_at'],
|
|
'staff' => api_staff_payload($staff),
|
|
]);
|