Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Cypress Azure AD Login
// In cypress/plugins folder
const puppeteer = require('puppeteer');
module.exports = {
debuggingPort: '',
setDebuggingPortMyService(port) {
[, debuggingPort] = port;
return null;
},
async aadLogin(options = {}) {
const username = options.username;
const password = options.password;
const appUrl = options.appUrl;
const emailSelector = "[name='loginfmt']";
const passwordSelector = '[name=passwd]';
const submitButtonSelector = 'input[type=submit]';
const browser = await puppeteer.connect({
browserURL: `http://localhost:${debuggingPort}`,
});
const page = await browser.newPage();
await page.goto(appUrl);
await page.waitForNavigation();
if (page.url().startsWith(appUrl)) {
// already logged in
page.close();
return {};
}
await page.waitForSelector(emailSelector);
await page.type(emailSelector, username);
await page.keyboard.press('Enter');
await page.waitForNavigation();
await page.waitForSelector(passwordSelector);
await page.focus(passwordSelector);
await page.waitFor(1000);
await page.type(passwordSelector, password);
await page.click(submitButtonSelector);
await page.waitForNavigation();
await page.waitForSelector(submitButtonSelector);
await page.click(submitButtonSelector);
await page.waitForNavigation();
await page.waitFor(2000);
await page.close();
return {};
},
};
// In cypress/plugins folder
const { setDebuggingPortMyService, aadLogin } = require('./aadLogin');
module.exports = (on, config) => {
// // `on` is used to hook into various events Cypress emits
// // `config` is the resolved Cypress config
on('before:browser:launch', (browser = {}, args) => {
const existing = args.args.find(arg => arg.slice(0, 23) === '--remote-debugging-port');
// Here you will need to persist the port to your plugins, whatever they may be
setDebuggingPortMyService(existing.split('='));
return args;
});
on('task', { aadLogin });
};
@Kishorekumar2789
Copy link

Kishorekumar2789 commented Jun 3, 2020

@rahulpnath Am facing an error like this
{project folder path}\node_modules\puppeteer\utils\browser\WebSocket' from '{project folder path}\node_modules\puppeteer\lib

I have installed puppeteer with this command: yarn add puppeteer & yarn add puppeteer.core

Also when I the code, the browser is not used anywhere
image

these are my doubts.. once it is done have to check the actual functionality!

@rahulpnath
Copy link
Author

rahulpnath commented Jun 3, 2020

@Kishorekumar2789 Shouldn't that go away if you add an _ before browser -> '_browser'?
Looks like it is not unused

@WvdE
Copy link

WvdE commented Jun 4, 2020

@Kishorekumar2789 What issue are you facing with the above code? not sure if any of the Microsoft form elements have changed but otherwise it should work. Looks like @WvdE had it working, except for the issue of clearing cookies

@WvdE Any particular reason you want to login for every test? Maybe you could try using puppeteer to clear the cookies if Cypress commands are having issues?

@rahulpnath it seems like I can't test compartmentalize my tests. What I used to do was:

Test functionality A
Step 1
Step 2
Step 3

Test Functionality B
Step 1
Step 2
Step 3

What occurs now, is that I cannot 'restart', so I cannot visit my baseURL, and now what happens is:
Test Functionality A
Step 1
Step 2
Step 3

Test Functionality B
Step 4
Step 5
Step 6

So I am continuing on where Test A stopped. What I am afraid of is that when A fails, B will automatically fail as well. As where as before, B wasn't dependent on A.

@rahulpnath
Copy link
Author

rahulpnath commented Jun 4, 2020

@WvdE Doesn't cy.visit() take you to the baseUrl again?

The only thing would be from the second test onwards you don't need to do the login since you will already have the cookie in the browser session. I have been using it like that on one of my projects.

Not sure if I fully understand the problem, but as long as you figure out a solution that works for you it is good!

@WvdE
Copy link

WvdE commented Jun 9, 2020

@WvdE Doesn't cy.visit() take you to the baseUrl again?

The only thing would be from the second test onwards you don't need to do the login since you will already have the cookie in the browser session. I have been using it like that on one of my projects.

Not sure if I fully understand the problem, but as long as you figure out a solution that works for you it is good!

@rahulpnath could you maybe show how you've used it? Because I can't seem to figure out as to why I can't open other cy.visit() pages. There is one instance in which, instead of opening a new tab, I extract the url from a hyperlink and visit that one:

extractAndVisitURL: function(selector){
 cy.get(selector)
 .invoke('attr', 'href')
 .then(href => {
   const travelerspage= 'https://test.net'+href
//    cy.visit(travelerspage)
   cy.visit({url: travelerspage})

In that case, Cypress tries to open a login.microsoft page, and the entire test case stops working - unsure why: "404 this page cannot be found"
Knipsel

@rahulpnath
Copy link
Author

rahulpnath commented Jun 10, 2020

@WvdE Is your base url correct and is the application visiting the correct URL? For auth I am assuming you are using cookies. Is there anything that is clearing the cookies in this case? How does your application behave when you do these scenarios manually? does it redirect you to login?

@WvdE
Copy link

WvdE commented Jun 10, 2020

@rahulpnath the base url is correct, and application is visiting the correct URL. I am using exactly what you have done, I didn't end up clearing the cookies or anything. When I do these manually they work fine. I feel like it is something really stupid I cannot figure out.

@rahulpnath
Copy link
Author

rahulpnath commented Jun 10, 2020

@WvdE Are you able to create a small repro that you can share? I can look if you have something like that. it's hard otherwise. Did you inspect the network tab of the tests to see what gets passed to the server and if the cookies are present etc? (Just some thoughts, maybe you have already :) )

@WvdE
Copy link

WvdE commented Jun 10, 2020

@rahulpnath Rahul!! I figured it out, I didn't have the env settings set correctly, so that's why it probably tweeked out the way that it did. Thank you for check in/helping me out!

@rahulpnath
Copy link
Author

rahulpnath commented Jun 11, 2020

@WvdE Awesome, great you figured it out!

@VGBenjamin
Copy link

VGBenjamin commented Jun 13, 2020

Hello,

I am also very interested in this. I am completelly new in cypress and I have this first challenge before being able to really learn cypress.
If you have the possibility to do a line by line how to, a small video or something I would really appreciate it.

Here is where I am currently (I am on windows). I have:

  • install cypress with the npm command
  • install puppeteer with the npm command
  • run the cypress interface with "cypress open"
  • copy the aadlogin.js file into the integration\plugins folder
  • I have copy the index in the integration\plugins folder

How do I set the debugging port? How could I debut the puppeteer script? I have try with debugger, with console.log, ... but I didn't see anything in the console. I have also tried to hardcode the port 9222 by using setDebuggingPortMyService(9222); it open a chome but I have the following message:
Failed to fetch browser webSocket url from http://localhost:9222/json/version: connect ECONNREFUSED 127.0.0.1:9222

You said that you needed to configure some stuffs how did you setup this?

Thank you

@rahulpnath
Copy link
Author

rahulpnath commented Jun 13, 2020

@VGBenjamin Thank you for your comment and idea to do a video. I do have a YouTube channel and do have a plan for this video soon. Meanwhile, there are a few other videos on Cypress if you are interested.

Will update here once the video is done (but not too soon though)

@VGBenjamin
Copy link

VGBenjamin commented Jun 14, 2020

Ok thank you. If you already have the possibility to tell me how to configure it in this post even without the video I would really appreciate it because we need to be able to authenticate to be able to test our project. Without that we are stuck.

REgards,

@MelvBa
Copy link

MelvBa commented Jun 18, 2020

@rahulpnath Thanks for this Gist. Most of the time it works, when running tests in GUI mode using:
npx cypress open --env username=MyUsername,password=MySecretPass .
I still have a problem with clearing cookies. It seems like the cookies for the login.microsoftonline.com domain are not cleared. I tried to search for a solution in puppeteer to clear the cookies, but did not find anything yet. Do you know how to do this?

Another problem I am facing is running the test using cypress run.
My situation:

  • I added a script to packages.json (testchromeheadless) which contains the following command: cypress run --browser chrome --headless
  • I removed the environment variables for username and password from cypress.json.
  • When running the test I add the environment variables to the command to run the tests:
    npm run testchromeheadless --env username=MyUsername,password=MySecretPass

It seems like there is a problem with passing these Environment variables in a command in combination with aadLogin.js.
I get the following error on the 'await page.type(emailSelector, username);' line of code in aadLogin.js:

CypressError: cy.task('aadLogin') failed with the following error:

 text is not iterable

Because this error occurred during a 'before all' hook we are skipping the remaining tests in the current suite: 'My spec'
      at http://localhost:65173/__cypress/runner/cypress_runner.js:153886:19
      at tryCatcher (http://localhost:65173/__cypress/runner/cypress_runner.js:10161:23)
      at Promise._settlePromiseFromHandler (http://localhost:65173/__cypress/runner/cypress_runner.js:8096:31)
      at Promise._settlePromise (http://localhost:65173/__cypress/runner/cypress_runner.js:8153:18)
      at Promise._settlePromise0 (http://localhost:65173/__cypress/runner/cypress_runner.js:8198:10)
      at Promise._settlePromises (http://localhost:65173/__cypress/runner/cypress_runner.js:8274:18)
      at _drainQueueStep (http://localhost:65173/__cypress/runner/cypress_runner.js:4868:12)
      at _drainQueue (http://localhost:65173/__cypress/runner/cypress_runner.js:4861:9)
      at Async.../../node_modules/bluebird/js/release/async.js.Async._drainQueues (http://localhost:65173/__cypress/runner/cypress_runner.js:4877:5)
      at Async.drainQueues (http://localhost:65173/__cypress/runner/cypress_runner.js:4747:14)
  From Your Spec Code:
      at Context.eval (http://localhost:65173/__cypress/tests?p=cypress\integration\Project\myspec.spec.js:18:8)

  From Node.js Internals:
    TypeError: text is not iterable
        at Keyboard.type (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\Input.js:112:28)
        at Keyboard.<anonymous> (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\helper.js:95:27)
        at ElementHandle.type (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\JSHandle.js:307:35)
        at processTicksAndRejections (internal/process/task_queues.js:93:5)
        at async DOMWorld.type (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\DOMWorld.js:292:9)
        at async aadLogin (C:\Cypress\ProjectTest\cypress\plugins\aadLogin.js:44:5)
    -- ASYNC --
        at ElementHandle.<anonymous> (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\helper.js:94:19)
        at DOMWorld.type (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\DOMWorld.js:292:22)
        at processTicksAndRejections (internal/process/task_queues.js:93:5)
        at async aadLogin (C:\Cypress\ProjectTest\cypress\plugins\aadLogin.js:44:5)
    -- ASYNC --
        at Frame.<anonymous> (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\helper.js:94:19)
        at Page.type (C:\Cypress\ProjectTest\node_modules\puppeteer\lib\Page.js:793:33)
        at aadLogin (C:\Cypress\ProjectTest\cypress\plugins\aadLogin.js:44:16)
        at processTicksAndRejections (internal/process/task_queues.js:93:5)

Does anyone know how I can fix this issue?

@rahulpnath
Copy link
Author

rahulpnath commented Jun 18, 2020

@MelvBa Did you check this to clear cookies with Puppeteer

@archanarachuri
Copy link

archanarachuri commented Jun 28, 2020

Hi Rahul! we use Auth0 with AAD login for our application..Your solution works very well..Thanks for that :) We have 2 factor auth implemented into our application now...Is there a way to solve that using puppeteer?

@rahulpnath
Copy link
Author

rahulpnath commented Jun 28, 2020

@archanarachuri Glad, it helped. I have not played around much with 2FA and tests. For the account I used, we had 2FA turned off (but I am not sure if that is a good practice)
Here are a few links that came up on a google search and looked promising -
Test automation approach for Two-factor authentication
Automated API Testing Strategy for Services using SMS-based Two-Factor Authentication

Do let know in case you find a way - I am interested

@archanarachuri
Copy link

archanarachuri commented Jun 28, 2020

Thanks Rahul! Sure I'll let you know

@TonyHernandezAtMS
Copy link

TonyHernandezAtMS commented Aug 25, 2020

This worked great for me! I like that it doesn't save anything to a file and instead just uses the same browser.

BTW - I used puppeteer-core instead of puppeteer and it worked great!
https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#puppeteer-vs-puppeteer-core

@rahulpnath
Copy link
Author

rahulpnath commented Aug 25, 2020

Thanks, @TonyHernandezAtMS for posting it back here!

@Coding-Means-Always-Learning

Hello guys,
can someone help me out here.
Did we try this code with a different browser other than for edge like electron .
What parameters are we required to pass to make this run on electron

I get the following error message when trying to run through electron.
image

@Coding-Means-Always-Learning
Copy link

Coding-Means-Always-Learning commented Mar 19, 2021

Hello guys,
can someone help me out here.
Did we try this code with a different browser other than for edge like electron .
What parameters are we required to pass to make this run on electron

I get the following error message when trying to run through electron.
image

I referred to another piece of code template in the following url and now its working for me using electron and chrome browser as well
Thank you very much @rahulpnath for sharing this article
https://gist.github.com/csuzw/845b589549b61d3a5fe18e49592e166f

@rahulpnath
Copy link
Author

rahulpnath commented Mar 19, 2021

Glad it helped and thank you for the comment @Coding-Means-Always-Learning

@Lakshmana-HN
Copy link

Lakshmana-HN commented Apr 12, 2021

Thank you @rahulpnath sharing this.
I tried with the above code you have added at the beginning here, but I am facing plugin file error like below
image

I have file index.js placed properly as mentioned.
is this something issue with my code, Please help me.

@rahulpnath
Copy link
Author

rahulpnath commented Apr 13, 2021

@Lakshmana-HN Hard to tell what the issue is here seeing this error. Looks like there is an error with the plugins file from the message. Were you able to figure it out?

@Lakshmana-HN
Copy link

Lakshmana-HN commented Apr 18, 2021

No, didn't find any solution for this, is it because of the some puppeteer or cypress version difference. I have "puppeteer": "^8.0.0", and "cypress": "3.4.1",

@oskoczypiec
Copy link

oskoczypiec commented Jul 2, 2021

Hi @rahulpnath !
First of all thank you for this code! It really helped as a starting point in authentication that I try to do using cypress. However I have a problem with logging into different azure ad accounts. It seems like it remebers my own account and whenever I visit website (I tried clearing cookies and localstorage; also used incognito mode) and try to log in into different test account it always redirects me to my url and I see that my account is logged in. Any help in tackling this issue would be great :)

@rahulpnath
Copy link
Author

rahulpnath commented Jul 7, 2021

@oskoczypiec Glad it helps. I didn't come across this issue though. Did you try in a different browser? Usually incognito does the trick for me and with these automated tests since it does not have anything saved to the browser state it works fine.

@devsrihari4
Copy link

devsrihari4 commented Aug 5, 2021

@rahulpnath, I am new to Cypress and I am trying to log into my application. Below are the steps manually to log in but I am unable to automate with Cypress.

These are manual steps to login

  1. Open the application and click on the login button which will redirect to identity server api
  2. Again click on login on the redirected site then it redirects to login.Microsoftonline.com and asks for credentials.
  3. Once credentials are verified it redirects to my application.

@Coding-Means-Always-Learning

@rahulpnath, I am new to Cypress and I am trying to log into my application. Below are the steps manually to log in but I am unable to automate with Cypress.

These are manual steps to login

  1. Open the application and click on the login button which will redirect to identity server api
  2. Again click on login on the redirected site then it redirects to login.Microsoftonline.com and asks for credentials.
  3. Once credentials are verified it redirects to my application.

Hi @devsrihari4,
I guess this should be possible by using puppeteer code suggested previously.

You need to identify which is the important cookie that will let the application know its the same user trying to login in again

You may want to do Step 1, 2 and 3 using puppeteer browser and once you are in the application you can return the cookies to cypress and save it and use it to login into the application

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment