false, "message" => "Username/password missing"]);
exit;
}
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);
$pdo = null;
qdb_discard($tmpDb, $lockFp);
if (!$user) {
http_response_code(401);
echo json_encode(["success" => false, "message" => "Invalid credentials"]);
exit;
}
if (!password_verify($password, $user['passwordHash'])) {
http_response_code(401);
echo json_encode(["success" => false, "message" => "Invalid credentials"]);
exit;
}
if (($user['role'] ?? '') === 'coach') {
http_response_code(403);
echo json_encode([
"success" => false,
"message" => "Coach accounts can only sign in through the mobile app.",
]);
exit;
}
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,
]);
echo json_encode([
"success" => true,
"user" => $username,
"role" => $user['role'],
"must_change_password" => true,
"temp_token" => $tempToken,
"message" => "Please change your password."
]);
exit;
}
$token = bin2hex(random_bytes(32));
token_add($token, 30 * 24 * 60 * 60, [
'role' => $user['role'],
'entityID' => $user['entityID'],
'userID' => $user['userID'],
]);
echo json_encode([
"success" => true,
"token" => $token,
"user" => $username,
"role" => $user['role'],
]);
} catch (Throwable $e) {
http_response_code(500);
echo json_encode(["success" => false, "message" => "Server error"]);
}