Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to login via Azure AD with cy.origin as cookies are blocked #25148

Closed
mikestock-nimble opened this issue Dec 14, 2022 · 37 comments
Closed
Labels

Comments

@mikestock-nimble
Copy link

mikestock-nimble commented Dec 14, 2022

Current behavior

Hi,

I've been using Puppeteer to login using Azure AD as Cypress was not able to load the page to perform the login

I have now upgraded to cypress 12 and I am able to load the screen and type in the user name, the problem is microsoft azure AD is saying cookies are disabled on the chrome browser that Cypress launched

It means that I can't complete the login as it breaks after the username is entered

image

Desired behavior

I would like to use Cypress to login via Azure AD, I assume the only issue here is the browser that is launched by Cypress has cookies disabled, so I need to know how to enable them if possible for Cypress

Test code to reproduce

    cy.origin(`https://login.microsoftonline.com`, () => {
        cy.visit("/");
        const username = Cypress.env('username');
        const password = Cypress.env('password');

        cy.get('[name="loginfmt"]').should("be.visible").type(`${username}{enter}`);
        cy.get('[name="passwd"]').should("be.visible").type(`${password}{enter}`);
        cy.get('[type="submit"]').click();
    });

Cypress Version

12.1.0

Node version

v16.19.0

Operating System

Windows 10 Enterprise

Debug Logs

No response

Other

No response

@nagash77 nagash77 self-assigned this Dec 14, 2022
@mschile
Copy link
Contributor

mschile commented Dec 14, 2022

@mikestock-nimble, thanks for logging this issue. Could you try enabling modifyObstructiveThirdPartyCode to see if that resolves your issue.

@mikestock-nimble
Copy link
Author

mikestock-nimble commented Dec 15, 2022

Hi @mschile

I forgot to mention I have the following config:

import { defineConfig } from 'cypress'

export default defineConfig({
  defaultCommandTimeout: 10000,
  pageLoadTimeout: 10000,
  watchForFileChanges: false,
  chromeWebSecurity: false,
  experimentalModifyObstructiveThirdPartyCode: true,
  reporter: 'cypress-multi-reporters',
  reporterOptions: {
    reporterEnabled: 'mochawesome',
    mochawesomeReporterOptions: {
      reportDir: 'cypress/reports/mocha',
      quite: true,
      overwrite: false,
      html: false,
      json: true,
    },
  },
  e2e: {
    // We've imported your old cypress plugins here.
    // You may want to clean this up later by importing these.
    setupNodeEvents(on, config) {
      return require('./cypress/plugins/index.js')(on, config)
    },
  },
})

I am really confused as to why it happens, because I checked the chrome that launched and it says it has cookies enabled, I'm not sure if it has something to do with cy.origin? Is there anything different about cy.origin and cookie permissions?

image

@mschile
Copy link
Contributor

mschile commented Dec 15, 2022

@mikestock-nimble, here is an example of Cypress using the boilerplate offered by Microsoft: #1342 (comment)

Does that work for you?

@mikestock-nimble
Copy link
Author

Hi @mschile

So I looked at that thread and it appears another user hit the same issue I did, I have also tried that solution in the thread posted by @AtofStryker and still no luck, it still thinks cookies are blocked even though my browser settings say otherwise

Thanks

@nagash77 nagash77 assigned mschile and unassigned nagash77 Dec 19, 2022
@mschile
Copy link
Contributor

mschile commented Dec 19, 2022

@mikestock-nimble, do you have a project I could look at? I believe the cookie check is Microsoft checking a cookie on document.cookie and not whether or not cookies are enabled in the browser config.

@mikestock-nimble
Copy link
Author

Hi @mschile I don't have one at at the moment that I can share, but I will investigate the document.cookie setting and do some reading around it

Thanks

@AtofStryker AtofStryker self-assigned this Jan 6, 2023
@AtofStryker
Copy link
Contributor

Hey @mikestock-nimble. I actually ran into this issue yesterday. Weirdly enough, it only happened when I input the wrong user credentials. I will open an issue to track this as well, but can you verify you are inputing the correct username and password?

@mikestock-nimble
Copy link
Author

@AtofStryker

Hi

I am entering in the correct user name, it fails before I get the chance to enter in a password

Thanks

@roelaenomar
Copy link

@AtofStryker I have the same issue and same cypress configuration. I will wait for the fix.
image
image

@AtofStryker AtofStryker added the Reproducible Can be reproduced label Jan 10, 2023
@AtofStryker
Copy link
Contributor

I am able to reproduce this issue only when I put in the wrong password when following pretty close with our proposed AAD guide

@guzmanoj
Copy link

guzmanoj commented Jan 12, 2023

Facing the same issue here!
I'm able to go a bit further using Electron instead of Chrome . Maybe Chrome is actually blocking the cookies?

Environment: Cypress 12.3.0 (using the zip download), Windows 11, Chrome v108, Electron v106.

Edit 1:
I was able to work-around the cookies issue using:

cy.window().then((win) => win.location.href='https://myapp.domain.io'); instead of cy.visit('https://myapp.domain.io')

But still, the authentication doesn't work and I can see a failing request in console:
(fetch)POST 400 https://login.microsoftonline.com/f86596e1-77d4-48b1-8gh4-3gb0cc7fa8f1/oauth2/v2.0/token

{
    "error": "invalid_request",
    "error_description": "AADSTS9002327: Tokens issued for the 'Single-Page Application' client-type may only be redeemed via cross-origin requests"
}

Edit 2:
So according to this StackOverflow post this could be related to a missing Origin header. Indeed! I checked the request on the Network tab, the Origin header was missing. Why? Because while trying to debug the initial issue (Chrome blocking cookies) I disabled the chromeWebSecurity 🤦🏻‍♂️

Now it seems to work (in Electron only), but I still need to use: cy.window().then((win) => win.location.href='https://myapp.domain.io');

Edit: 3
Microsoft uses this helper utility to check whether cookies are enabled or not:

image

I put some debugging logic and it returns false, however navigator.cookieEnabled returns true.
And this weird behavior only occurs in Chrome, Electron works fine.

@guzmanoj
Copy link

@AtofStryker is there any update on this?

@AtofStryker
Copy link
Contributor

@guzmanoj no updates yet, but hoping to investigate this hopefully this week

@guzmanoj
Copy link

@guzmanoj no updates yet, but hoping to investigate this hopefully this week

I could help with some debugging and also willing to provide more details :)

@AtofStryker
Copy link
Contributor

@guzmanoj we would very much appreciate that! My guess is that there is a bit of a race condition between the cookie helper check and when Cypress sets the cookie. For cy.origin, we have to patch document.cookie in order to get the cookies established correctly. But this sometimes can happen async, which might lead to a race condition. However, for this case it should be happening synchronously. Do you know which file the Microsoft CookiesHelper utility lives in? It might be good to see if the cookie is actually being written to suggest cookies are enabled for Microsoft's utility. And compare that to what is actually in document.cookie.

@guzmanoj
Copy link

guzmanoj commented Jan 20, 2023

@AtofStryker
Sure. The utility itself lives in js/Core/BrowserControl.js around line 761.
Then there's a function called isRedirectNeeded() in charge of performing the redirect (document.location = serverData.urlNoCookies) based on cookies enabled flag is located in js/LoginPage/PaginatedLoginPage.js this function is being called on window load event according to line 28 of the PaginatedLoginPage.js

image

Would be interesting to put a break-point somewhere here and see if the patch is actually working in this scenario.

@AtofStryker
Copy link
Contributor

@guzmanoj thank you for pointing me in the right direction! So I spent some time digging today and I think there is a bug in our cookie patch:

So to check if cookies are enabled, Microsoft sets a cookie called CkTst that is set to a value and then checks if that cookie is truthy/available.

Immediately after checking if that cookie exists (which it does), the cookie is then removed. But the way this cookie is removed is by setting the value to "" and setting the expiry time to a value in the past from now (in this case, a date from 1980). So the cookie is set, and then immediately removed, which has the delete effect.

We are not checking the expiry time of the cookie before setting it, which means the cookie is being set in the jar when it isn't supposed to be:

To test this theory, I added a naive patch to the cookie patch to remove a cookie in the jar if its past expiry (if exists) and do not set it. This seems to fix the issue with the only know way I have to test it, which is putting in a wrong password.

Before adding the naive patch

azure-not-working-660.mp4

After adding the naive patch

azure-working-660.mp4

I'm going to route this issue over to the e2e team as I think we can come up with a fix for this.

@mikestock-nimble
Copy link
Author

@guzmanoj thank you for pointing me in the right direction! So I spent some time digging today and I think there is a bug in our cookie patch:

So to check if cookies are enabled, Microsoft sets a cookie called CkTst that is set to a value and then checks if that cookie is truthy/available.

Immediately after checking if that cookie exists (which it does), the cookie is then removed. But the way this cookie is removed is by setting the value to "" and setting the expiry time to a value in the past from now (in this case, a date from 1980). So the cookie is set, and then immediately removed, which has the delete effect.

We are not checking the expiry time of the cookie before setting it, which means the cookie is being set in the jar when it isn't supposed to be:

To test this theory, I added a naive patch to the cookie patch to remove a cookie in the jar if its past expiry (if exists) and do not set it. This seems to fix the issue with the only know way I have to test it, which is putting in a wrong password.

Before adding the naive patch

azure-not-working-660.mp4

After adding the naive patch

azure-working-660.mp4
I'm going to route this issue over to the e2e team as I think we can come up with a fix for this.

Awesome work! That definitely looks like the right solution!

@guzmanoj
Copy link

Definitely cool stuff @AtofStryker
Looking forward to give it a shot on a real environment. Thank you!

@AtofStryker
Copy link
Contributor

possibly related to #25194

@guzmanoj
Copy link

@AtofStryker is it possible that you publish here the new cypress_cross_origin_runner.js? I believe that your naive patch should live there after compiling all the sources, if so I could just simply replace that file on my local cypress installation 😃
Or what about zipping the entire resources\app\packages\runner\dist just to be sure?

@reshadatavid
Copy link

Can this be related to :#25605

If so, I would also like to test the patch

@AtofStryker
Copy link
Contributor

@guzmanoj I am going to work on getting a binary you can try that you can install through npm. I will update when it's available. What OS are you on again?

@AtofStryker
Copy link
Contributor

Can this be related to :#25605

If so, I would also like to test the patch

@reshadatavid It is possible. I should be able to investigate that issue today

@AtofStryker
Copy link
Contributor

@guzmanoj @mikestock-nimble you should be able to see the binaries for this patch here for each OS. Let me know how it goes!

@guzmanoj
Copy link

guzmanoj commented Jan 28, 2023

@AtofStryker Still not working on my end unfortunately.
I completely removed all of the old Cypress stuff and installed it again from the link you provided.
Also, the package.json of the installed Cypress in my project's node_modules indicates that I'm really using your patch. This is also true for my project's package.json (see images)
So I was doing some extra debugging... and here comes the potentially dummy question: Can we make sure that the code you introduced is actually doing its magic? My initial thought was that it could be part of the cypress_cross_origin_runner.js because I noticed that this file is being loaded for the origin login.microsoftonline.com, but maybe the document.cookie monkey-patching happens at an early stage... if so, could it be possible to sneak around (e.g. which files should I look at?)

image

image

image

@AtofStryker
Copy link
Contributor

@guzmanoj Interesting. So that document.cookie monkey patch should be happening on the loaded resource from login.microsoftonline.com since we inject cypress and our patches into the top of the HTML for the file that loads subsequent resources.

This puts us back in the spot of being able to reproduce this issue since I no longer can on my end. Would you be able to possibly invite me to a production repository, even if it's private?

The other option is for you to to try and debug this locally with the cypress develop branch, which might be a bit of a challenge if you are on windows (assuming from the binary download). You could try it though by forking cypress and running yarn in the root directory, which may take some time to complete. Once that is done, you can make these changes to try and debug the cross-origin injection easier, followed by yarn cypress:open to start the test running in global mode and pointing it to your project. You could be able to debug the same files with this that I showed above, hopefully 😅 .

@AtofStryker
Copy link
Contributor

would anyone be willing to try this with 12.7.0 with chromeWebSecurity set to true (yes actually turning it on 😅 )?

@AtofStryker
Copy link
Contributor

I am going to close this issue due to inactivity. If this is still happening, please comment on this issue or open another issue.

@vinkms
Copy link

vinkms commented Mar 24, 2023

@AtofStryker I had a similar issue when I try to login via Azure AD, the API call to get the token fails (error 400) after redirecting from login.domain.com to myapp.domain.net but then it got fixed when I set chromeWebSecurity to true as you suggested. If you could kindly share information on why this is happening when chromeWebSecurity is set to false because our other tests requires that to be set to false. Thanks

@AtofStryker
Copy link
Contributor

@vinkms I think the main reason the 400 was happening was due to a missing origin header. What is your use case for requiring chromeWebSecurity to be turned off?

@vinkms
Copy link

vinkms commented Mar 24, 2023

@AtofStryker it's for some tests that involves a separate application embedded in an iframe. Thanks for the explanation.

@AtofStryker
Copy link
Contributor

@vinkms of course! I wonder if you may be able to get around it by adding the expected origin header in a cy.intercept?

@laztwo
Copy link

laztwo commented Apr 20, 2024

Hello guys, i still have the same problem.
Current behavior:

When using the cy.origin('login.microsoftonline.com') or even cy.visit command in Cypress, a window pops up indicating that cookies are blocked, preventing further interaction with the Microsoft login page.

image

Desired behavior:

When a user is logged in using the website's authentication system, they should have the option to connect via Microsoft to access additional features on the website. This option should be presented clearly and should initiate the Microsoft authentication flow seamlessly, allowing the user to authenticate with their Microsoft account and access the additional features without any issues.

Test code to reproduce:

//commands.js
Cypress.Commands.add('microsoftLogin', () =>
{
    cy.origin('login.microsoftonline.com', () => {
        cy.get('input[type="email"]').type(Cypress.env("MICROSOFT_USERNAME"))
        cy.get('input[type="submit"]').click()
        cy.get('input[type="password"]').type(Cypress.env("MICROSOFT_PASSWORD"), {
            log: false,
        })
        cy.get('input[type="submit"]').click()
    })
})
//cypress.config.js

experimentalModifyObstructiveThirdPartyCode: true

Cypress Version: 13.7.3

Node version: v20.12.1

Operating System: Windows 11

@iepoch
Copy link

iepoch commented Apr 23, 2024

@laztwo Question are you in incognito mode when running the test. The latest update of Chrome I noticed sets the incognito mode to disable third-party cookies. When this setting is turned off the test run through fine. However, with Cypress is frames you actually page so the top level or base url you have set might have cookies that block this;. IE
image

I been running into the same problem and this seems too look like the framing that is done with Cypress is causing the issue with Active Directory. I have turned on Chrome Websecurity I have also tried setting other options. The only way I have found around it has been to add specific sites allowed for the third party cookies. But Framing AD is not the way to go in this test from what I cant tell. It might be better off if you update the browser with a token.
image

@laztwo
Copy link

laztwo commented Apr 26, 2024

Hello @iepoch thank you for your answer. i have updated chrome and cypress to the last version and in my tests i don't use incognito mode, u checked if in cypress chrome browser i have third-party cookies disabled but it wasn't.
I already set experimentalModifyObstructiveThirdPartyCode to true and i tried chromeWebSecurity on true and false but i get the same result : cookies blocked...
I don't know if i should open a new github issue or not because am new to github issue platform and i see that this ticket is still closed

@iepoch
Copy link

iepoch commented May 8, 2024

@

Hello @iepoch thank you for your answer. i have updated chrome and cypress to the last version and in my tests i don't use incognito mode, u checked if in cypress chrome browser i have third-party cookies disabled but it wasn't. I already set experimentalModifyObstructiveThirdPartyCode to true and i tried chromeWebSecurity on true and false but i get the same result : cookies blocked... I don't know if i should open a new github issue or not because am new to github issue platform and i see that this ticket is still closed

You could open a new ticket but I noticed your going to cy.origin('login.microsoftonline.com')
Your base URL must redirect you to Login.microsoftonline.com correct? I am assuming it does. This was the only way I was able to redirect without being in a constant loop. Try going to a site within the function but make sure your beforeEach has a site first. Its odd but I have seen this before because when routing to a origin it can not be the first site you visit it can not be the top level.

I have the same setup but had to do a work around like
BaseUrl: google.com

My Test Function:

  beforeEach(() => {
    cy.visit('/spa/signin');
  });


  it('Valid Azure Active Directory User Signin', () => {
    cy.loginToAAD(`${AD_URL}${siteDomain}`, 'test@test.com');
  });

My Helper Function:

export const loginViaAAD = (site: string, email: string, password: string) => {
  cy.visit(site);  // This is the redirect site that will redirect to login.microsoftonline.com
  cy.origin(
    'login.microsoftonline.com',
    {
      args: {
        email, password,
      },
    },
    ({ email, password }) => {
      cy.get('input[type="email"]').type(email, {
        log: false,
      });
      cy.get('input[type="submit"]').click();
      cy.get('input[type="password"]').type(password, {
        log: false,
      });
      cy.get('input[type="submit"]').click();
      cy.get('input[type="submit"]').click();
    },
  );

Also add this to Launch Options in Cypress Config
This helps with if your on a network and your already signed into AD

 setupNodeEvents (on, config) {

      on('before:browser:launch', (browser={
        name: '',
        family: 'chromium',
        channel: '',
        displayName: '',
        version: '',
        majorVersion: '',
        path: '',
        isHeaded: true,
        isHeadless: false,
      }, launchOptions) => {
        if (browser.family === 'chromium') {
          launchOptions.args.push('--auth-server-allowlist=_');
        }
        return launchOptions;
      });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants