Skip to content

Instantly share code, notes, and snippets.

@saschwarz
Last active August 31, 2022 07:16
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saschwarz/b7f115ab6a7765ff5e45b9b9461cf895 to your computer and use it in GitHub Desktop.
Save saschwarz/b7f115ab6a7765ff5e45b9b9461cf895 to your computer and use it in GitHub Desktop.
Automated Azure AD login and session token capture to json file for reading by Cypress commands. Heavily inspired by https://gist.github.com/pieterdv/82773fbe036719479d76ab0a4985dc3b
const API = Cypress.env('API');
const headers = {
Authorization: '',
};
Cypress.Commands.add('loginUser', () => {
return cy.readFile('aad-tokens.json')
.then(creds => {
// set auth headers so test setup calls are authorized
headers.Authorization = `Bearer ${creds['msal.idtoken']}`;
// put MS Azure AD creds in session storage
// so application under test will be logged in.
for (let key in creds) {
if (
key.startsWith('msal.') ||
key.startsWith('{"authority":')
) {
sessionStorage[key] = creds[key];
}
}
});
});
// example of custom command using headers configured via loginUser
Cypress.Commands.add('APICreate', (url, instance) => {
Cypress.log({
name: 'APICreate',
message: url + ' | ' + JSON.stringify(instance)
})
return cy.request({
method: 'POST',
headers: headers,
url: `${API}/${url}`,
body: instance
}).then(data => data.body);
});
const fs = require('fs');
const puppeteer = require('puppeteer');
const commander = require('commander');
const inquirer = require('inquirer');
const util = require('util');
const readFile = util.promisify(fs.readFile);
const getAdToken = function(config) {
console.log(`Logging in ${config.email} at ${config.appURI}`);
return puppeteer.launch({ headless: true }).then(async browser => {
try {
const page = await browser.newPage();
await page.goto(config.appURI);
await page.waitFor(3000);
await page.click('input[name=passwd]');
await page.type('input[name=loginfmt]', config.email, {
delay: 50
});
await page.waitFor(500);
await page.click('input[type=submit]');
await page.waitFor(500);
await page.click('input[name=passwd]');
await page.waitFor(500);
await page.type('input[name=passwd]', config.password, {
delay: 50
});
await page.waitFor(500);
await page.click('input[type=submit]');
await page.waitForSelector('.nav-link', { visible: true, delay: 3000 });
const aadValues = await page.evaluate((conf) => {
for (let i = 0, len = sessionStorage.length; i < len; ++i) {
if (
sessionStorage.key(i).startsWith('msal.') ||
sessionStorage.key(i).startsWith('{"authority":')
) {
conf[sessionStorage.key(i)] = sessionStorage.getItem(
sessionStorage.key(i)
);
}
}
return conf;
}, config);
browser.close();
fs.readFile('aad-tokens.json', 'utf8', (err, data) => {
fs.writeFile('aad-tokens.json', JSON.stringify(aadValues), { encoding: 'utf8' },
(error) => {
if (error) {
console.log(error);
}
});
});
console.log(`aad-tokens.json updated`);
} catch (error) {
console.log(error);
browser.close();
}
});
};
const promptUser = async function(defaults) {
const config = await inquirer.prompt([{
name: 'appURI',
message: 'App URI:',
default: defaults.appURI
},
{
name: 'email',
message: 'Email:',
default: defaults.email
},
{
name: 'password',
message: 'Password:',
default: defaults.password
},
]);
await fs.writeFile('./aad-tokens.json',
JSON.stringify(config), { encoding: 'utf8' },
(error) => {
if (error) {
console.log(error);
}
});
return config;
};
const loginUser = function(prompt = false) {
Promise.resolve().then(async() => {
let config = await readFile('./aad-tokens.json', 'utf-8')
.then(file => JSON.parse(file))
.catch(e => {
return {
appURI: 'http://localhost:4200/app/',
email: '',
password: '',
};
});
if (prompt) {
config = await promptUser(config);
}
getAdToken(config);
});
};
const main = () => {
commander
.option('--no-prompt', 'Do not prompt for input and accept values from aad-tokens.json', false)
.parse(process.argv);
loginUser(commander.prompt);
};
module.exports = {
getAdToken: getAdToken,
loginUser: loginUser,
};
main();
...
"scripts": {
"cypress": "node getadtoken.js --no-prompt && cypress open --env API=https://example.com/data/v4",
"login": "node getadtoken.js"
},
...
@nikkieta
Copy link

I am trying to login to https://login.microsoftonline.com using puppeter above mentioned code. getadtokens.js
Calling LoginUser in my spec in Cypress. using below code.

describe('Homepage', () => {
it('Does not do much!', function() {
cy.loginUser();
});
});
Nothing happens in Cypress test runner. Not able to see the UI which we have already logged in using Commands.js.
Please share if i m missing anything. How can we proceed with the test cases. even we tried adding cy.visit("https://test.ci.ai.dynamics.com") after cy.loginUser(); but got error as microsoftonline refused to connect.

@saschwarz
Copy link
Author

Sorry @nikkieta I'm no longer working on that project and no longer have an environment to test.

Things to check:

  1. that you actually have a valid token in the file

  2. you see the token is in header of the test API calls when you inspect them in Cypress

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