cypress: Azure AD B2C bad request when redirecting / using cy.origin()

Current behavior

We are trying to do a login with cypress, and we tried to use the “cy.origin” command. In local this worked well, as the domain of the login was a complete different one. Once we launched this in the pipeline, started complaining cypress that the “super domain” was the same one ( same error as this issue ).

As cypress identifies the login domain as the same domain, we assumed that then we could use the cypress commands directly in that page, so we did the following condition so that the code would be executed with origin or without depending of the environment:

 const loginFunction = ({ username, password, emailInput, passwordInput, nextButton }) => {
    cy.get(emailInput, { timeout: 5000 }).should('be.visible').type(username, { log: false });
    cy.get(passwordInput).type(password, { log: false });
    cy.get(nextButton).click();
  };

  // When localhost, we need the "origin command"
  if (Cypress.config('baseUrl').includes('localhost')) {
    cy.origin(
      loginOrigin,
      {
        args: {
          username: usernameForLogin,
          password: passwordForLogin,
          emailInput: LoginPage.emailInput,
          passwordInput: LoginPage.passwordInput,
          nextButton: LoginPage.nextButton
        }
      },
      loginFunction
    );
  } else {
    // When deployed, we don't need the "origin command"
    loginFunction({
      username: usernameForLogin,
      password: passwordForLogin,
      emailInput: LoginPage.emailInput,
      passwordInput: LoginPage.passwordInput,
      nextButton: LoginPage.nextButton
    });
  }

Problem comes that when we are doing the login in the CI ( not using the cy.origin ), one response of loading a page returns a 400 ( usually returns a 302 that page - With cy.origin or by normal usage of the login )

Same code executed in localhost, and using the cy.origin works just perfectly

cypress-login-issue-ra-develop-failing

Desired behavior

That the code works the same with or without origin

Test code to reproduce

Provided in the description

Cypress Version

12.5.1

Node version

18.13.0

Operating System

Windows 10 19042.2486

Debug Logs

No response

Other

SSO for the login used is Azure AD B2C

Frontend application is Angular with @azure/msal-angular

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 6
  • Comments: 59 (17 by maintainers)

Most upvoted comments

+1 we really need this to be fixed

we have to bend over backwards to fix many of our tests and really can’t test for certain things when using workarounds mentioned

@AtofStryker Works like a charm =). Also checked it with a testcase where we test the complete password reset flow, which previously was failing and now works with the build from the branch.

@Xan0C This is really helpful. I have a feeling the cookie isn’t getting set in the browser in your case since its cross-origin / same-site, and the cookie jar logic doesn’t think it needs to be simulated since it is same-site, when in fact it does need to be simulated.

I wonder if we need to actually store the full origin string and then check to see if there is a sub domain in the simulation code so we still simulate cookies correctly on cross-origin / same-site requests from the AUT if the url of the AUT is an origin match.

could you try the changes in this branch and see if they also work for you? https://github.com/cypress-io/cypress/compare/same-origin-match-for-cookie-jar?expand=1

@mjhenkes similar on our side. We have an Angular app that uses angular-auth-oidc-client (oauth code flow) + Azure AD B2C.

We have our app available on: app.domain.xyz Our B2C login page is sitting on: login.domain.xyz

Everything works fine when we run Cypress and it tests the app running on localhost (and using cy.origin):

  1. we go to our app on localhost
  2. we aren’t authorized so the app redirects us to the B2C login page (login.domain.xyz)
  3. we have 2 steps form on the B2C side (the first page is where you type your login only, then there is a request to B2C and if everything is fine with the login, there is the second page, where we put the password.
  4. if everything is ok, there is a redirect to localhost from the login.domain.xyz with the token.

When we run tests with the app that is available on app.domain.xyz and we remove cy.origin (because of the same super domain Cypress error):

  1. we go to app.domain.xyz
  2. the app redirects to the B2C login page: login.domain.xyz
  3. on the B2C login page we can input the login (the first step in the B2C login form)
  4. request goes to the B2C and it redirects us to our app (app.domain.xyz) with the error message: AADB2C90255 B2C error (_The claims exchange specified in technical profile ‘{0}’ did not complete as expected).

I believe that it’s a similar case to @mmonteiroc but we have a little different B2C configuration. When we wrap the actions on the B2C login page with cy.origin (inside cy.session) everything works fine. But when we remove cy.origin, B2C fails.

Hi @madalinbucur152. Sorry for the delayed response as I have been on leave for a few months. I kicked off a build based off develop that is running now on b67b0ee

Hi @AtofStryker can you please rebuild the patch as the old link retention policy kicked in.

its currently building on https://github.com/cypress-io/cypress/commit/c7af4ad735758cbb9451ae34f080f9b52a3e7872

Hi @AtofStryker! Thanks for the fix! Would it be possible to have a permanent fix for this issue? It is quite unpleasant that once at 60 days (when pre-release build is not available anymore) to have our tests fail in the pipeline and be forced to downgrade to an older version - 10.9.0 - (which is the last working version) until a new pre-release build is available. Do you think you can help us? Thank you!

We have the same problem in our use case: Using cy.origin works when the login domain is under a different origin e.g. test.testlogin.com and application is under e.g. test.myapp.com On our prod system we have login.myapp.com and myapp.com and therefore can not use cy.origin. We did a workaround to simply repeat the failing bad request which does use the corrected cookie on the second try:

cy.intercept('GET', `${getLoginUrl()}/*/B2C_1A_signup_signin_oauth_jds/api/CombinedSigninAndSignup/confirmed?**`).as(
    'loginRequest'
  );
  loginAzure(username, password);
  // Workaround, since it can happen that cypress does not set the correct cookies on the initial login request
  cy.wait('@loginRequest').then((resp) => {
    if (resp.response?.statusCode === 400) {
      cy.request({
        method: 'GET',
        url: resp.request.url,
        headers: resp.request.headers,
      }).then((authResp) => {
        // reload the page with the updated session data
        expect(authResp.status).to.eq(200);
        cy.visit('');
      });
    }
  });

Without cy.origin the set-cookie header does not get applied correctly between 2 consecutive requests. I think this issue is related #26040 #25841

After some digging yesterday evening I’ve found that the response-middleware behaves differently if the login domain uses a different origin.

https://github.com/cypress-io/cypress/blob/9517def6b7a95a975b5eadc9454ff0541c8eca0e/packages/proxy/lib/http/response-middleware.ts#L432-L438

Removing those lines and therefore saving the cookie in the CookieJar solves the issue for our use case. I am not really sure why exactly this helps so at this point i am just guessing, it looks like “though-cookie” is responsible to sync the cookies between requests? And when using the same origin the call to https://github.com/cypress-io/cypress/blob/9517def6b7a95a975b5eadc9454ff0541c8eca0e/packages/proxy/lib/http/response-middleware.ts#L456 does not happen.

https://github.com/cypress-io/cypress/blob/9517def6b7a95a975b5eadc9454ff0541c8eca0e/packages/proxy/lib/http/util/cookies.ts#L240 https://github.com/cypress-io/cypress/blob/9517def6b7a95a975b5eadc9454ff0541c8eca0e/packages/server/lib/util/cookies.ts#L122

@mjhenkes @AtofStryker I just have done what @mjhenkes asked me, and for what I see, the requests are almost identical between both domains ( deployed / localhost ) The only difference, is the cookie itself, as is encoded depending on the timing, etc… but the same amount of cookies are sent, same payload, etc

I’ve the exact same setup and problem as @sebastiandenis: my Angular app is hosted on app.domain.com, my B2C login page is hosted on login.domain.com and MSAL returns error AADB2C90255. Also, upgrading Cypress to 12.7.0 or enabling experimentalSkipDomainInjection does not work…

Same behaviour in our side @flotwig @sebastiandenis

We face the issue only when using same “root domain” ( which is our case so we need to keep it like so ).

When we are outside of the same domain ( localhost for instance ), and we have to use cy.origin works perfectly fine ! So same question as @sebastiandenis, can we force the usage of cy.origin ?? Or maybe tell cypress how to consider a different origin?

/ping @mjhenkes as I see that @flotwig removed it self and added you 😃

@mmonteiroc we have encountered a similar issue. We also use Azure AD B2C.

Everything works fine for localhost and cy.origin wrapped around B2C SSO page. But when we open the app on an actual domain for example app.domain.xyz and we have our B2C login page on login.domain.xyz, we can’t use cy.origin (the same super domain) and we get the AADB2C90255 B2C error (_The claims exchange specified in technical profile ‘{0}’ did not complete as expected. You might want to try starting your session over from the beginning).

I have tried almost everything, still without success 😕