false, "message" => "Bearer token required"]); exit; } $tokenRec = token_get_record($bearerToken); if (!$tokenRec) { http_response_code(403); echo json_encode(["success" => false, "message" => "Invalid or expired token"]); exit; } if ($username === '' || $oldPassword === '' || $newPassword === '') { http_response_code(400); echo json_encode(["success" => false, "message" => "Missing fields"]); exit; } if (strlen($newPassword) < 6) { http_response_code(400); echo json_encode(["success" => false, "message" => "New password too short (min 6 characters)."]); exit; } // Verify the token belongs to the user requesting the change if (($tokenRec['userID'] ?? '') === '') { http_response_code(403); echo json_encode(["success" => false, "message" => "Token not associated with a user"]); exit; } 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); http_response_code(401); echo json_encode(["success" => false, "message" => "Invalid credentials."]); exit; } if ($user['userID'] !== $tokenRec['userID']) { qdb_discard($tmpDb, $lockFp); http_response_code(403); echo json_encode(["success" => false, "message" => "Token does not match user"]); exit; } if (($user['role'] ?? '') === 'coach') { qdb_discard($tmpDb, $lockFp); http_response_code(403); echo json_encode([ "success" => false, "message" => "Coach accounts can only sign in through the mobile app.", ]); exit; } if (!password_verify($oldPassword, $user['passwordHash'])) { qdb_discard($tmpDb, $lockFp); http_response_code(401); echo json_encode(["success" => false, "message" => "Old password incorrect."]); exit; } $newHash = password_hash($newPassword, PASSWORD_DEFAULT); $upd = $pdo->prepare( "UPDATE users SET passwordHash = :h, mustChangePassword = 0 WHERE userID = :uid" ); $upd->execute([':h' => $newHash, ':uid' => $user['userID']]); $pdo = null; qdb_save($tmpDb, $lockFp); $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, "message" => "Password changed.", "token" => $token, "user" => $username, "role" => $user['role'], ]); } catch (Throwable $e) { if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp); http_response_code(500); echo json_encode(["success" => false, "message" => "Server error"]); }