cypress: Change of origin fails on all browsers except Firefox

Current behavior

In the attached spec I’m registering at my site then attempting to log into https://ethereal.email to check for the registration email. The registration succeeds but logging into Ethereal fails with “Error invalid csrf token” at the Ethereal site using any browser other than Firefox.

Desired behavior

Logging into Ethereal.email should succeed after the registration process on all browsers.

Test code to reproduce

  1. Using the attached script, rename .txt to .js.
  2. Use a baseURL of https://test-hecho-en-todos-santos.pantheonsite.io
  3. Create an account at Ethereal and change this line in the spec: const userEmail = ‘adaline.littel54@ethereal.email’
  4. Run the script with a browser other than Firefox.
  5. You should see the error. But if you restart back at line 3 and use Firefox it will work.

origin_test.cy.txt

CypressOriginProblem.log.txt

Cypress Version

12.9.0

Node version

16.16.0

Operating System

macOS 13.3.

Debug Logs

Debug output included above.

Other

No response

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 19 (3 by maintainers)

Most upvoted comments

Thanks for providing this, @hankszeto. I just pinged Cypress in Discord to bring it to their attention, the goal being to get a solution into the main Cypress build.

@alfonsodev @aangelinsf

The hint is in the warning provided by Chrome:

This Set-Cookie header didn’t specify a “SameSite” attribute and was defaulted to “SameSite=Lax,” and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The Set-Cookie had to have been set with “SameSite=None” to enable cross-site usage.

image

It looks like the reason why this is happening is because:

  • the domain on the cookies (ethereal.email) differs from the address in the URL bar of the Cypress test runner (which is the address of my local DDEV instance, https://myway-3.ddev.site:8443/)
  • the Set-Cookie response from https://ethereal.email/login does not specify SameSite attribute, so “SameSite=Lax,” is assumed and is therefore blocked in Chrome-based browsers.
set-cookie: _csrf=Trbhry3d5QoKO5KN5o89CzzC; Path=/
 set-cookie: webmail=s%3AFqzO0KxPAF2M0UR5ctxc3W6wSdkZrBoD.7n7e8mQ1IcvK4KXi7dgd%2Buw9j3KYuPgvZLpaoCj6yn0; Path=/; HttpOnly

I got this working by intercepting the Set-Cookies header and enforcing SameSite=None; Secure.

Here is the code:

    cy.intercept('*', (req) => {
      req.on('response', (res) => {
        const setCookies = res.headers['set-cookie'];

        if (setCookies) {
          res.headers['set-cookie'] = (
            Array.isArray(setCookies) ? setCookies : [setCookies]
          )
            .map((cookie) => {
              // Override or add SameSite=None
              if (cookie.includes('SameSite')) {
                cookie = cookie.replace(/SameSite=(Lax|Strict|None)/i, 'SameSite=None');
              } else {
                cookie = `${cookie}; SameSite=None`;
              }

              // Add Secure if not present
              if (!cookie.includes('Secure')) {
                cookie = `${cookie}; Secure`;
              }

              return cookie;
            });
        }
      });
    });

    cy.origin('https://ethereal.email', { args: sentArgs }, ({
      userEmail, userPassword, searchSubjectLine,
    }) => {
.
.
.

@aangelinsf Ah I see. Good call. I have simplified your example down even further to see if testing the Ethereal login flow is an issue in Cypress.

This login appears it works fine outside of cy.origin()

  it("ethreal login without cy.origin", () => {
    cy.visit('https://ethereal.email/login', true).then( () => {
      const userEmail = 'aidan.cole84@ethereal.email'
      const userPassword = '4WTrxM2T9vnPtqKYTA'

      cy.getCookie('_csrf').should('exist')
      cy.getCookie('webmail').should('exist')
      
      cy.get('#address').type(userEmail)
      cy.get('#password').type(userPassword)
      
      cy.get('form > :nth-child(5) > .btn').click()
      cy.contains('Logged in as ' + userEmail)
    })

It seems like something is getting dropped / missing when using cy.origin() specifically with visiting Ethereal. I will flag as a bug and route to the team.