new system prototype

This commit is contained in:
2026-04-15 10:19:42 +02:00
parent e805f225bc
commit 034b108c7e
80 changed files with 12212 additions and 890 deletions

150
handlers/auth.php Normal file
View File

@ -0,0 +1,150 @@
<?php
switch ($route) {
case 'auth/login':
if ($method !== 'POST') {
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
}
$body = read_json_body();
$username = trim($body['username'] ?? '');
$password = (string)($body['password'] ?? '');
if ($username === '' || $password === '') {
json_error('MISSING_FIELDS', 'Username and password are required', 400);
}
try {
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
$stmt = $pdo->prepare(
"SELECT userID, passwordHash, role, entityID, mustChangePassword
FROM users WHERE username = :u"
);
$stmt->execute([':u' => $username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
qdb_discard($tmpDb, $lockFp);
if (!$user || !password_verify($password, $user['passwordHash'])) {
json_error('INVALID_CREDENTIALS', 'Invalid username or password', 401);
}
if ((int)$user['mustChangePassword'] === 1) {
$tempToken = bin2hex(random_bytes(32));
token_add($tempToken, 10 * 60, [
'role' => $user['role'],
'entityID' => $user['entityID'],
'userID' => $user['userID'],
'temp' => true,
]);
json_success([
'mustChangePassword' => true,
'token' => $tempToken,
'user' => $username,
'role' => $user['role'],
]);
}
$token = bin2hex(random_bytes(32));
token_add($token, 30 * 24 * 60 * 60, [
'role' => $user['role'],
'entityID' => $user['entityID'],
'userID' => $user['userID'],
]);
json_success([
'token' => $token,
'user' => $username,
'role' => $user['role'],
]);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp);
error_log($e->getMessage());
json_error('SERVER_ERROR', 'Server error', 500);
}
break;
case 'auth/change-password':
if ($method !== 'POST') {
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
}
$bearerToken = get_bearer_token();
if (!$bearerToken) {
json_error('UNAUTHORIZED', 'Bearer token required', 401);
}
$tokenRec = token_get_record($bearerToken);
if (!$tokenRec) {
json_error('FORBIDDEN', 'Invalid or expired token', 403);
}
$body = read_json_body();
$username = trim($body['username'] ?? '');
$oldPassword = (string)($body['old_password'] ?? '');
$newPassword = (string)($body['new_password'] ?? '');
if ($username === '' || $oldPassword === '' || $newPassword === '') {
json_error('MISSING_FIELDS', 'username, old_password, and new_password are required', 400);
}
if (strlen($newPassword) < 6) {
json_error('PASSWORD_TOO_SHORT', 'New password must be at least 6 characters', 400);
}
if (($tokenRec['userID'] ?? '') === '') {
json_error('FORBIDDEN', 'Token not associated with a user', 403);
}
try {
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
$stmt = $pdo->prepare(
"SELECT userID, passwordHash, role, entityID FROM users WHERE username = :u"
);
$stmt->execute([':u' => $username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
qdb_discard($tmpDb, $lockFp);
json_error('INVALID_CREDENTIALS', 'Invalid credentials', 401);
}
if ($user['userID'] !== $tokenRec['userID']) {
qdb_discard($tmpDb, $lockFp);
json_error('FORBIDDEN', 'Token does not match user', 403);
}
if (!password_verify($oldPassword, $user['passwordHash'])) {
qdb_discard($tmpDb, $lockFp);
json_error('INVALID_CREDENTIALS', 'Old password incorrect', 401);
}
$newHash = password_hash($newPassword, PASSWORD_DEFAULT);
$pdo->prepare(
"UPDATE users SET passwordHash = :h, mustChangePassword = 0 WHERE userID = :uid"
)->execute([':h' => $newHash, ':uid' => $user['userID']]);
qdb_save($tmpDb, $lockFp);
// Revoke the old (possibly temp) token and issue a fresh one
token_revoke($bearerToken);
$newToken = bin2hex(random_bytes(32));
token_add($newToken, 30 * 24 * 60 * 60, [
'role' => $user['role'],
'entityID' => $user['entityID'],
'userID' => $user['userID'],
]);
json_success([
'token' => $newToken,
'user' => $username,
'role' => $user['role'],
]);
} catch (Throwable $e) {
if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp);
error_log($e->getMessage());
json_error('SERVER_ERROR', 'Server error', 500);
}
break;
default:
json_error('NOT_FOUND', 'Unknown auth route', 404);
}