How to work on 2FA when I am trying to log in into an account using Cypress? - cypress

I want to log in into an account, but I am receiving 2FA and to confirm the new device I am getting emails in my inbox, and I am not able to login in into account.
Anyone, can you please tell me how to handle this or if I can do something with MailSlurp in Cypress?
I short, I want to open the website, fill in username, pw, and login into the account successfully even after I get 2FA dialog box popping out, where the 2FA confirmation email is getting into my email inbox.
Thanks in advance and I appreciate your help.
Best,
Preeti D

MailSlurp is fine but you can use Twilio as well, here is my working example source code
const accountSid = 'AC793683c4982a14f01714321bd3f90ca7';
const authToken = '819068e54369ac58bb8aad976fa517bc';
const githubEmail = 'your_github_email'
const githubPassword = 'your_github_password'
describe('Login with github credentials', () => {
beforeEach(()=>{
cy.visit('https://github.com/login');
cy.get('#login_field').type(githubEmail);
cy.get('#password').type(githubPassword);
cy.get('input[type="submit"]').click()
})
it('Get SMS and apply it in 2FA form', () => {
cy.request({
method: 'GET',
url: `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`,
auth: {
username: accountSid,
password: authToken,
AuthMethod: 'BasicAuth',
}
})
.its('body').then((res) => {
cy.wait(1500) //wait for SMS
const otpcode = res.messages[0].body.substring(0, 6)
cy.get('#otp').type(otpcode);
cy.url().should('eq', 'https://github.com/');
})
});
});

Related

Cypress cy.visit() is not redirecting to correct URL

I am trying to bypass the UI login by using cy.request() to log a user in and cy.visit() to go to the restricted route. I have followed this doc: https://docs.cypress.io/guides/end-to-end-testing/testing-your-app#Bypassing-your-UI
However, the test fails because the visit URL ('http://localhost:3000/join-or-create') is not loaded and instead the home page URL is loaded (http://localhost:3000/).
This is my test code:
describe('when user is signed in but has not joined a group', () => {
beforeEach(() => {
cy.request('POST', 'http://localhost:5000/api/testing/reset');
const user = {
name: 'Joe Bloggs',
email: 'Joe#Bloggs.com',
password: 'Password',
};
cy.request('POST', 'http://localhost:5000/register', user);
cy.request('POST', 'http://localhost:5000/sign-in', user);
cy.visit('http://localhost:3000/join-or-create');
});
it.only('should allow a logged in user to join or create a group', () => {
cy.contains('Join a group');
cy.contains('Create a group');
});
});
If I change cy.contains('Join a group'); to cy.contains('Welcome'); (which is content on the URL 'http://localhost:3000/') then the test passes.
If I use:
cy.visit('http://localhost:3000');
cy.get('[data-testid="email"]').type('Joe#Bloggs.com');
cy.get('[data-testid="password"]').type('Password');
cy.contains('sign in').click();
instead of cy.visit('http://localhost:3000/join-or-create'); the test passes.
The output of the test body shows that is redirecting to a new URL 'http://localhost:3000/' (as shown in the screenshot below) but I can't figure out why.
Thanks for any help.
In the bypass code, check the response from the POST request
cy.request('POST', 'http://localhost:5000/sign-in', user)
.then(response => console.log(response))
to see what tokens are returned.
Then in the form-login code, look at what happens to the same token after cy.contains('sign in').click() and see where the browser stores same token.
That's probably the step missing in the bypass code. You'll need to add something to do the same, e.g
cy.request('POST', 'http://localhost:5000/sign-in', user)
.then(response => {
const token = response.body.token; // figure out where the token is in response
cy.setCookie(token); // figure out where the app want's the token
})
It's also difficult to tell from the question what URL you need to cy.visit() in the bypass code, but there's only a couple of them so try both permutations.

Cypress basic authentication in all cy.visit requests

I have a cy.visit() calls with basic authentication which looks like this:
it('launch website', () => {
cy.visit('url', {
auth: {
username: '....',
password: '....',
},
})
And it works fine. But in order to make this code more useable, I dont want to hard code my credentials in every test, but I want to create a command so every time I visit that page, it uses my basic auth. I tried to implement solutions from Adding basic auth to all requests in Cypress , but it didnt work for me. It doesnt accept credentials that are stored in command. file. Any ideas other then those already mentioned?
Many thanks :)
You can use Cypress custom commands for this.
Go to cypress/support/commands.js and wrte:
Cypress.Commands.add('authenticateUrl', (url, user, pass) => {
cy.visit(url, {
auth: {
username: user,
password: pass,
},
})
})
In your test you can just write this. You can pass url, username and passwords as parameters.
cy.authenticateUrl('https://example.com', 'admin', 'pass123')
In case you don't want to use paramters, you can directly harcode url, username and password inside the custom command.
Cypress.Commands.add('authenticateUrl', () => {
cy.visit('https://example.com', {
auth: {
username:'admin',
password:'pass123',
},
})
})
In your test just use:
it('launch website', () => {
cy.authenticateUrl() //Launch URL and authenticate
})
This can be avoided by overwriting the visit function. Add this to cypress/support/index.js
Cypress.Commands.overwrite('visit', (originalVisit, url) => {
originalVisit(url, {
auth: {
username: 'username',
password: 'password'
}
})
});
Notice that this code only working with cy.visit(url). If you pass another arguments you should change it according to your situation.

How to send specific user ID from bot framework emulator v4?

I need to send a specific user ID from the bot emulator (https://github.com/microsoft/BotFramework-Emulator). I use this textbox (see on a picture below)
But nothing sent. There is absolutely another guid in activity.From.Id.
Is it possible to sent message from emulator with a specific user ID?
The short answer is, if you are using Direct Line to generate a token from a secret and you specify user Id there (see attached code), then Emulator should favor that value over any value you pass in thru the Emulator settings.
In my personal testing, the User ID setting seems to be overriding any other pre-existing value.
It should be noted, however, that if you specify a User ID value in settings, you will need to close the tabbed conversation and start it anew by re-entering the messaging endpoint and AppId/AppPassword (or reconnecting to your .bot file, if used). Simply pressing "Restart conversation" will not cause Emulator to pickup the User ID setting.
Hope of help!
// Listen for incoming requests.
server.post('/directline/token', (req, res) => {
// userId must start with `dl_` for Direct Line enhanced authentication
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${ process.env.directLineSecret }`
},
json: {
user: {
Id: `${ userId }`
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone requested a token...');
} else {
res.status(500).send('Call to retrieve token from DirectLine failed');
}
});
});

Google Sign-in User Changed Listener

When user lands on page, the Google auth is loaded and initiated. The Google Sign-in API provides a listener function that's triggered if the user changes (GoogleAuth.currentUser.listen()). The expected behavior for the feature that I want to implement involves watching for users logging into gmails within other tabs in the browser.
const userChanged = user => {
if (user) {
console.log('CHANGED TO USER: ', user);
}
};
const loadGoogleAuth = () => {
window.gapi.load('auth2', () => {
const gapiAuth = window.gapi.auth2;
gapiAuth.init({
client_id: '<YOUR-CLIENT-ID>.apps.googleusercontent.com',
cookie_policy: 'none', //my hunch is this could be affecting listener capabilities?
scope: 'profile email openid',
});
const authInstance = gapiAuth.getAuthInstance();
const element = document.getElementById('googleSignIn');
authInstance.attachClickHandler(
element,
{},
googleUser => {
console.log(`Signed in: ${googleUser.getBasicProfile().getName()}`);
},
error => {
console.log('Sign-in error', error);
}
);
authInstance.currentUser.listen(userChanged);
});
};
const withLifeCycles = lifecycle({
componentDidMount() {
if (window.gapi) {
loadGoogleAuth();
}
},
});
This is working, but only for the first gmail that the user signs in with or the gmail that is already signed in before the user lands on the page. In other words, if I log in with Gmail A, it detects the user change but it won't detect Gmail B if I log out of Gmail A and log into Gmail B afterwards. However, it will detect relogins for Gmail A subsequently and not for any other gmail addresses. Is it possible for the listener function to detect sign-ins on any gmail address or only for the firs gmail that is detected?
You need to listen to signed-in changes too. That way you will detect that user signed out from account A and signed in to B.
auth2.isSignedIn.listen(signinChanged)
see https://developers.google.com/identity/sign-in/web/listeners

Bypass UI Login using Cypress

I am having problem bypassing UI login. My web application doesn't use API to authenticate users. There are no endpoints like /login. index.php will just open the login page and submit the form to login.
The application authenticate the user by
auth($_REQUEST['username'], $_REQUEST['password_tx']);
This is what cypress printed after UI login submit.
I have no idea how to move on from here.
// This doesn't work. The application doesn't get the user details from the body. It is in the submitted form.
cy.request({
method: 'POST',
url: '/index.php?p=sys001',
form: true,
body: {
username: 'user',
password_tx: 'pass'
}
})
This is the complete testcase for the issue. Added comments to make them understandable.
it("login via form spoof", () => {
cy.get("div#mDiv > form").invoke("attr", "action").then(($action) => { //get
the attribute of 'action' and pass encoded uname and pwd to it
let username = Cypress.env("username");
let password = Cypress.env("password");
cy.intercept("POST", $action, (req) => { //post request and populate body
// intercepting the POST form to spoof it.
req.body = $action + encodeURIComponent(username)+ encodeURIComponent(password)
})
.as("loginForm"); //alias
});
cy.get("div#mDiv > div.login > form")
.submit(); //Submit the form after locating it.
});

Resources