Open Closed

Cypress POST Requests Returning 400 Bad Request – Works Fine in Browser #9502


User avatar
0
Rishi1532 created

Hi ABP Support Team,

We are currently facing an issue while running Cypress automation tests in our ABP-based application.

Setup Details:

  • We have deployed the project with two separate subdomains:

      * One for the frontend (Angular).
    
      * Another for the backend (ABP MVC API).
    
  • The application works perfectly when accessed through a regular browser (manually).

  • We are using the Cypress for automated end-to-end testing.

Problem:

When performing POST requests via Cypress (e.g., creating a role), we consistently receive a 400 Bad Request response from the backend API. However, the same operations succeed when done manually through the UI in a regular browser.

Observations:

  • GET requests work fine through Cypress.

  • The issue only arises for POST/PUT operations (e.g., creating roles).

  • Screenshot and Cypress test snippet are attached for reference. /// <reference types="cypress" /> import { Data } from "../../../fixtures/data.model";

describe('Administration > Security Management > Roles', { scrollBehavior: false }, () => { let data: Data; const baseUrl = Cypress.config().baseUrl; const testData = { role_name: "cypress role " + new Date().getTime(), role_sidebar_text: 'Roles', role_sidebar_url: '/identity-management/roles' } before(function () { cy.fixture("example").then((res) => { data = res; }); });

beforeEach(() => {

    cy.visit({ url: baseUrl, failOnStatusCode: false });
    cy.visit({ url: baseUrl, failOnStatusCode: false });
    cy.contains("Login", { matchCase: false, timeout: 30000 })
        .eq(0)
        .should("exist")
        .scrollIntoView()
        .click({ force: true });

    cy.wait(1000);
    // Input creds 
    cy.get('[id="LoginInput_UserNameOrEmailAddress"]').type(data.username, { delay: 20 });
    cy.get('[id="password-input"]').type(data.password, { delay: 20 });
    cy.wait(1000);

    cy.contains("Login", { matchCase: false, timeout: 30000 })
        .eq(0)
        .should("exist")
        .scrollIntoView()
        .click({ force: true });

    cy.contains("Home", { matchCase: false, timeout: 30000 })
        .eq(0)
        .should("exist")
        .scrollIntoView()
        .click({ force: true });
    cy.wait(1000);
    cy.log("✅ Passed ")

});

after(() => {
    // cy.clearLocalStorage();
    // cy.clearCookies();
});

it("should create a role", () => {
    const exactRegex = new RegExp(^${testData.role_sidebar_text}$, 'i');
    cy.log("✅ Visiting -> " + exactRegex)
    cy.contains(exactRegex, { matchCase: false, timeout: 30000 })
        .should('exist')
        .eq(0)
        .scrollIntoView()
        .click({ force: true });

    cy.wait(1000);

    cy.url().should('include', testData.role_sidebar_url);
    cy.wait(1000);

    // On role page
    cy.contains("New role", { matchCase: false, timeout: 30000 })
        .eq(0)
        .should("exist")
        .scrollIntoView()
        .click({ force: true });

    cy.get(".modal-content", { timeout: 20000 })
        .contains("New role", { matchCase: false })
        .should("exist")
        .scrollIntoView()
    cy.wait(2000)

    cy.get('.modal-content', { timeout: 10000 })
        .contains('label', 'Role name', { matchCase: false }) // find the label by text
        .parent()                            // go to the parent of the label
        .find('input')                       // find the input inside that parent
        .first()                             // select the first input (if multiple)
        .scrollIntoView()
        .type(testData.role_name, { force: true });

    cy.get(".modal-content", { timeout: 20000 })
        .contains("Public", { matchCase: false })
        .should("exist")
        .scrollIntoView()
        .click({ force: true });

    const createRole = "createRole"
    // cy.intercept("POST", "/api/identity/roles/").as(createRole);
    cy.wait(1000)
    cy.contains("Save", { matchCase: false, timeout: 30000 })
        .eq(0)
        .should("exist")
        .scrollIntoView()
        .click({ force: true });
    // cy.wait("@" + createRole, { timeout: 60000 }).its("response.statusCode").should("eq", 200);
    cy.wait(1000)
    cy.log("❌ tests failing due to server's incorrect response")
})


it("should look for a role", () => {
    const exactRegex = new RegExp(^${testData.role_sidebar_text}$, 'i');
    cy.log("✅ Visiting -> " + exactRegex)
    cy.contains(exactRegex, { matchCase: false, timeout: 30000 })
        .should('exist')
        .eq(0)
        .scrollIntoView()
        .click({ force: true });

    cy.wait(1000);

    cy.url().should('include', testData.role_sidebar_url);
    cy.wait(1000);

    const getRole = "getRole"
    cy.intercept("GET", "/api/identity/roles**").as(getRole);
    // On role page
    cy.get('input[placeholder="Search"]')
        .should('exist')
        .eq(0)
        .scrollIntoView()
        .type('cypress', { force: true });
    cy.wait("@" + getRole, { timeout: 60000 }).its("response.statusCode").should("eq", 200);
    cy.get('[role="table"]')
        .contains('cypress')
        .eq(0)
        .scrollIntoView()
        .should('be.visible');

});

});

Request:

Could you please assist us in identifying what may be causing this issue specifically for Cypress and how to resolve it within the ABP framework setup (e.g., configuration changes, headers, etc.)?

Thank you in advance for your help!

Best regards, Tuniki Rishikesh AUMTECH


7 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • The issue you are experiencing with POST/PUT requests returning 400 Bad Request in Cypress, while working fine in the browser, is commonly related to anti-forgery (CSRF/XSRF) token validation or cookie handling in ABP applications.

    Solution Steps

    1. Antiforgery Token Issue
      ABP uses antiforgery tokens for POST/PUT/DELETE requests. When using Cypress, the required antiforgery token may not be sent or may not match, especially across subdomains.

      • Ensure Cypress is sending the XSRF-TOKEN cookie and the corresponding request header (RequestVerificationToken or X-XSRF-TOKEN) with your POST/PUT requests.

      • If your frontend and backend are on different subdomains, set the antiforgery cookie's domain to the parent domain in your backend configuration:

        Configure<AbpAntiForgeryOptions>(options =>
        {
            options.TokenCookie.Domain = ".yourdomain.com"; // Use your actual parent domain
        });
        
    2. Cookie Secure Policy
      If you are running tests over HTTP (not HTTPS), you must allow the antiforgery cookie to be sent over HTTP:

      Configure<AbpAntiForgeryOptions>(options =>
      {
          options.TokenCookie.SecurePolicy = CookieSecurePolicy.None;
      });
      
    3. Cypress Configuration

      • Make sure Cypress is configured to preserve cookies between requests and is not blocking or clearing them.
      • You may need to manually extract the antiforgery token from the cookies and set it as a header in your Cypress POST/PUT requests.
    4. WebDAV and IIS
      If you are hosting on IIS, ensure the WebDAV module is disabled, as it can block non-GET requests:

      • Remove the WebDAV module from your IIS configuration.
    5. SameSite Cookie Policy
      Modern browsers and test runners may enforce SameSite cookie policies. If your app is running on different subdomains, set the antiforgery cookie's SameSite policy to Lax or None as appropriate:

      Configure<AbpAntiForgeryOptions>(options =>
      {
          options.TokenCookie.SameSite = SameSiteMode.Lax; // or SameSiteMode.None if using HTTPS
      });
      

    References (validated)


    Summary:
    The most likely cause is antiforgery token or cookie configuration issues between Cypress and your ABP backend, especially with subdomains. Adjust your AbpAntiForgeryOptions as shown above, ensure cookies and headers are handled correctly in Cypress, and check for IIS/WebDAV interference if applicable.


    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    Rishi1532 created

    hi we have used the recommendations from the answer you have provided added this in the HttpApiHostModule > configureServices method Configure<AbpAntiForgeryOptions>(options => { options.TokenCookie.Domain = ".aumtech.org"; // Use your actual parent domain options.TokenCookie.SecurePolicy = CookieSecurePolicy.None; options.TokenCookie.SameSite = SameSiteMode.Lax; // or SameSiteMode.None if using HTTPS });

    we still received the same error

    and 4th point id not applicable as we are using data center Linux server for hosting

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share the 400 error debug logs?

    See https://abp.io/support/questions/8622/How-to-enable-Debug-logs-for-troubleshoot-problems

    Thanks

  • User Avatar
    0
    Rishi1532 created

    Hi,

    Thank you for the response.

    We’ve enabled debug logging as per the instructions provided in this guide.

    Could you please let us know the preferred way to share the debug log files with you for the 400 error issue we’re encountering?

    Looking forward to your guidance.

    Best regards, Rishikesh Tuniki

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can share it with liming.ma@volosoft.com
    Or https://wetransfer.com/

    Thanks.

  • User Avatar
    0
    Rishi1532 created

    Hi,

    Thank you for the information.

    We’ve collected the debug logs for issue #9502 – "Cypress POST Requests Returning 400 Bad Request – Works Fine in Browser" and have sent them directly via email to liming.ma@volosoft.com.

    Please confirm once received. Let us know if any additional details are needed.

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    The error is The required antiforgery header value "RequestVerificationToken" is not present.

    The POST HTTP request needs the RequestVerificationToken header. The RequestVerificationToken value is coming from Cookies.

    https://abp.io/docs/latest/framework/infrastructure/csrf-anti-forgery#the-solution

    Thanks.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on July 09, 2025, 06:20