Why does my CSRF token validation fail when deleting multiple entries in PHP?

I’m encountering an issue with CSRF token validation in my PHP application. Specifically, when I try to delete multiple entries using the same CSRF token, I receive a “CSRF attack detected” error message. I believe this issue is related to the CSRF token being used multiple times within the same session without being regenerated.

  <?php
session_start();

// Generate a new CSRF token if one doesn't exist
if (!isset($_SESSION['auth_token'])) {
    $_SESSION['auth_token'] = bin2hex(random_bytes(20));
}

// Handle delete request
if (isset($_POST["delete_entry"])) {
    // Validate CSRF token
    if (!isset($_POST['auth_token']) || !isset($_SESSION['auth_token']) || $_SESSION['auth_token'] !== $_POST['auth_token'] || !isset($_POST['target_id'])) {
        // If the CSRF token is invalid or missing
        echo '<div class="alert alert-danger alert-dismissible fade show" role="alert">
                <i class="fas fa-bug"></i>
                CSRF attack detected. Please reload the page and try again.<button type="button" class="btn-close"
                data-bs-dismiss="alert" aria-label="Close"></button>
              </div>';
        
        // Regenerate CSRF token to prevent any further attempts with the same token
        $_SESSION['auth_token'] = bin2hex(random_bytes(20));
        exit();
    } else {
        // Proceed with delete operation if the CSRF token is valid
        $target_id = htmlspecialchars(strip_tags($antiXss->xss_clean($_POST['target_id'])));
        $stmt = $con->prepare("SELECT * FROM jentress_erp WHERE jnum = ? and company=?");
        $stmt->bind_param("ss", $target_id, $_SESSION["company"]);

        $stmt->execute();
        $result = $stmt->get_result();

        if ($result->num_rows > 0) {
            while ($row = $result->fetch_assoc()) {
                $_id = $row["id"];
                $_path = $row["position"]; 
                
                $stmt1 = $con->prepare("SELECT * FROM entress_erp_part2 WHERE contact_id = ?");
                if ($stmt1 === false) {
                    die('Prepare failed: ' . htmlspecialchars($con->error));
                }
                $stmt1->bind_param("i", $_id);
                $stmt1->execute();
                $result = $stmt1->get_result();
                if ($result->num_rows > 0) {
                    while ($row = $result->fetch_assoc()) {
                        $_account = htmlspecialchars($antiXss->xss_clean($row['s_account']), ENT_QUOTES, 'UTF-8');
                        $_acc_serial_token = htmlspecialchars($antiXss->xss_clean($row['acc_serial_token']), ENT_QUOTES, 'UTF-8');
                        $_debtor = htmlspecialchars($antiXss->xss_clean($row['s_debtor']), ENT_QUOTES, 'UTF-8');
                        $_creditor = htmlspecialchars($antiXss->xss_clean($row['s_creditor']), ENT_QUOTES, 'UTF-8');

                        $stmtu = $con->prepare("SELECT * FROM categories WHERE acc_serial =? AND acc_name=? AND company=?");
                        $stmtu->bind_param("sss", $_acc_serial_token, $_account, $_SESSION["company"]);
                        $stmtu->execute();
                        $resultu = $stmtu->get_result();
                        if ($resultu->num_rows > 0) {
                            $rowu = $resultu->fetch_assoc();
                            $balance = $rowu['ac_balanced'];
                            $new_balance = $balance - $_debtor + $_creditor;

                            $stmtu2 = $con->prepare("UPDATE categories SET ac_balanced=? WHERE acc_serial=? AND acc_name=? AND company=?");
                            $stmtu2->bind_param('ssss', $new_balance, $_acc_serial_token, $_account, $_SESSION["company"]);

                            if ($stmtu2->execute()) {
                                $stmt5 = $con->prepare("DELETE FROM jentress_erp WHERE id = ? AND company=?");
                                $stmt5->bind_param("is", $_id, $_SESSION["company"]);
                                if ($stmt5->execute()) {
                                    if (isset($_path) && file_exists($_path)) {
                                        unlink($_path);
                                    }
                                    echo '<script>$(document).ready(function(){toastr.success("Entry successfully deleted");}) </script>';
                                } else {
                                    echo '<script>$(document).ready(function(){toastr.error("Error occurred while deleting the entry");}) </script>';
                                }
                            } else {
                                echo '<script>$(document).ready(function(){toastr.error("Error occurred while updating the balance");}) </script>';
                            }
                        } else {
                            echo '<script>$(document).ready(function(){toastr.error("No data found");}) </script>';
                        }
                    }
                } else {
                    echo '<script>$(document).ready(function(){toastr.error("Entry number error");}) </script>';
                }
            }
        } else {
            echo '<script>$(document).ready(function(){toastr.error("No data found for this number");}) </script>';
        }

        // After successful deletion, generate a new token
        $_SESSION['auth_token'] = bin2hex(random_bytes(20));
    }
}
?>

When trying to delete multiple entries in a single request or making successive delete requests within the same session, the CSRF token validation fails. The error message indicates a potential CSRF attack. This suggests that the CSRF token is being reused or is not being updated properly, causing subsequent requests to fail the CSRF check.
**What I Need Help With:
**
. Understanding why the CSRF token validation is failing when attempting to delete multiple entries.
. Suggestions on how to properly handle CSRF tokens in such scenarios to ensure that multiple deletions can be performed in a single session without issues.
. Best practices for CSRF token management in PHP applications to avoid similar issues.
Thank you!

<!-- Modal -->
                <div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" 
                 data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
                  <div class="modal-dialog">
                    <div class="modal-content">
                      <div class="modal-header bg-info">
                        <h1 class="modal-title fs-5" id="staticBackdropLabel">Are you sure to delete the entry?</h1>
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                      </div>
                      <div class="modal-body">
                      <form method="POST" action="">
                        <div class="mb-3">
                        <input type="hidden" name="auth_token" value="<?php echo htmlspecialchars(strip_tags($antiXss->xss_clean($_SESSION['auth_token']))); ?>" required>
                          <input type="hidden" name="target_id" class="form-control" id="recipient-name">
                        </div>
                      </div>
                      <div class="modal-footer">
                        <button type="submit" name="delete_entry" class="btn btn-danger"><i class="fa-solid fa-trash"></i> delete </button>
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><i class="fa-solid fa-ban"></i> cancel</button>
                      </div>
                    </div>
                    </form>
                  </div>
                </div>