I have an application to test that requires MFA. I am trying to get my UI tests through Cypress to hit the application already authenticated.
I've seen a few posts about setting up a new Cypress command to handle logging in where it sends auth details to the 3rd party, gets the details back and puts this into local storage using something like cy.setLocalStorage.
But I already have an external method that I use for my API tests where it grabs me a valid Bearer token. This works fine for API calls on the application. So I'm wondering, is there a place I can simply insert this valid token for my UI tests with Cypress or do I need to go the kind of way as defined in the linked article below where I build a cy.login() command?
Edit: Should add that we actually have a service principal account for the API calls to bypass MFA.
https://auth0.com/blog/end-to-end-testing-with-cypress-and-auth0/
Without having more information about the exact login and your application, if the token for your API tests is the same and sufficient for the login, then you can probably store it in the local or session storage depending on where your app is looking for the valid token.
The only important thing then is that you do this before the first cy.visit command to show your app that you are already authenticated / logged in like:
describe('Your Test', () => {
it('Login and page visit', () => {
// or sessionStorage.setItem()
localStorage.setItem('your_token_name', yourToken);
cy.visit('your app url')
})
})
However, I'm not sure if the MFA requires additional login steps that you might not cover with it. If in doubt, you can also think about disabling MFA for a test user used in your Cypress tests.
Besides that, as you've already written, it's often a good way to log in via request to avoid having to test third-party UIs that you have no control over changing, such as here an example for Azure AD Login.
You can also add session caching to the login command, or a wrapper command for your external method that grabs the token.
The request is only fired once, then the results are cached so the same token is restored each time you call the command.
Cypress.Commands.add('login', (username, password) => {
cy.session([username, password], () => {
cy.request({
method: 'POST',
url: '/login',
body: { username, password },
}).then(({ body }) => {
window.localStorage.setItem('authToken', body.token)
})
})
})
Related
I just want to retrieve the Cognito user attributes and other details by using a valid jwt access token produced when the user signed in earlier.
I use Nodejs and want to know whether there is a way to do this with the aws-amplify library.
We can decode the JWT using a third-party library like jwt-decode. The payload contains the details themselves.
There is a method in aws-amplify library that you can use for this purpose.
import { Auth } from 'aws-amplify';
Auth.currentAuthenticatedUser({
bypassCache: false // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
}).then(user => console.log(user))
.catch(err => console.log(err));
You will see attributes field in the response's payload.
If you face any problems, please check the official documentation
This method can be used to check if a user is logged in when the page is loaded. It will throw an error if there is no user logged in. This method should be called after the Auth module is configured or the user is logged in.
UPDATED 9/16:
I've reworded my question. I'm trying to use cypress to test a work application that has an Angular frontend(http://localhost:4200) and a .NET Core backend (http://localhost:5000).
When the app starts, a login page loads with username and password fields. The cypress code test fills in the username and password and clicks the submit button, as show in my code below.
When the login (submit) button is clicked, it triggers a POST request to the .NET Core backend. The request submits the username and password and if the user is verified, a token comes back in response. The token is added to session storage and the user is logged in. It is this token value in the session storage that signifies the user is logged in. With the token added, the user gets redirected to the homepage.
Now the backend is NOT running. I need to simulate this POST request so the cypress test can get to the actual homepage.
I think I need to stub this POST request but I can't get this code to work. The request just aborts and looking at the console it says the request was not stubbed.
Here's my updated cypress code:
const username = 'johndoe';
const password = 'pass1234';
cy.server();
cy.get('h1').should('contain', 'Login');
cy.get('input[placeholder=Username]').type(username);
cy.get('input[placeholder=Password]').type(password);
cy.get('button[type=submit]').click();
cy.route({
method: 'POST',
url: 'http://localhost:5000/Authentication/Login',
response: {
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' // some random characters
}
});
You might have more success if you set up the route before invoking the POST (clicking submit),
cy.server();
cy.route({
method: 'POST',
url: 'http://localhost:5000/Authentication/Login',
response: {
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' // some random characters
}
});
...
cy.get('button[type=submit]').click();
However, if your app uses native fetch as opposed to XHR, core Cypress does not catch this type of request. You have to add a polyfill which essentially modifies the window.fetch method to allow cy.route() to succeed.
cypress.json
{
"experimentalFetchPolyfill": true
}
Please note the limitations section,
Limitations
The experimental polyfill is not foolproof. It does not work with fetch calls made from WebWorkers or ServiceWorker for example. It might not work for streaming responses and canceled XHRs. That's why we felt the opt-in experimental flag is the best path forward to avoid breaking already application under tests.
I am trying to create a web app whose main task is fixing appointment.
I do not want to access any mail data of the logged in user.
I only want to implicitly login using an outlook account (my account) to which I have admin access. I want to connect with this account, fetch its calendar events and display the events to the logged in user so that the user can select any available spots.
I have registered my app in the azure portal and provided all the application permissions (earlier I tried with Delegated permissions as well; but I guess delegated permissions are not for my use case).
Thereafter, I tried to fetch the token for my profile using:
this.http.post(`https://login.microsoftonline.com/f8cdef31-a31e-4b4a-93e4-5f571e91255a/oauth2/v2.0/token`,
{
client_id: 'my-client-uuid',
scope: 'https://graph.microsoft.com/.default',
grant_type: 'client_credentials',
client_secret: '****myclientsecret****'
},
{
headers: {
Host: 'login.microsoftonline.com',
'Content-type': 'application/x-www-form-urlencoded'
}
}
).subscribe(resp => {
console.log(resp);
});
as suggested in this article.
However, my request fails while doing this and states that the request body must contain 'grant_type' when I am clearly sending that.
Can someone please suggest me how I can implicitly get data from my own outlook account in a web app.
Update: I used the suggestion from this, appears that the request is going through now. However, the browser throws CORS error saying that the server didn't have appropriate headers.
Update 2: Found this link, which seems to address the exact issue I am facing. I however already have the redirect URI for SPA. The issue still persists.
I'm unable to authenticate / sign-in via AzureAD when running testCafe.
const testrole = Role(
'https://login.microsoftonline.com/',
async t => {
await t
.typeText(Selector('input').withAttribute('type', 'email'), *******)
.click(Selector('#idSIButton9'))
.typeText(Selector('input').withAttribute('type', 'password'), ********)
.click(Selector('#idSIButton9'));
},
{ preserveUrl: true }
);
The above steps work fine, however after entering the password I get a message saying:
"Unable to sign in to Outlook account, Error: AADSTS900561: The endpoint only accepts POST requests. Received a GET request."
From my initial search, it seems like something to do with 3rd party cookies on the browser. However, I'm unable to find a solution at this time.
Any idea how I get around this issue?
The Azure AD product team has always reminded me that it is a bad idea to try to automate sign in like that.
They will probably detect that you are a bot and start blocking your requests, even if you succeed.
Instead, to acquire access tokens you need to use either the client credentials flow (for app-only tokens) or the resource owner password credentials flow (for delegated user tokens).
Client credentials flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
ROPC flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
You have to take good care to secure the credentials used for testing.
And use a test tenant if possible.
I'm trying to setup a manual flow for Facebook login, as per the docs at: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/
I've got my test Facebook app working as expected, i.e., I can login using a private web browser window fine. The URL I'm using is:
https://facebook.com/v3.3/dialog/oauth?client_id=<app_id>&display=popup&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html
Now within my React-Native app, I'm using react-native-inappbrowser-reborn to present a SFAuthenticationSession on iOS. As per their docs (at https://www.npmjs.com/package/react-native-inappbrowser-reborn), I'm doing the following:
const redirectUri = "https://www.facebook.com/connect/login_success.html"
const url = "https://facebook.com/v3.3/dialog/oauth?client_id="+appId+"&display=popup&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html"
InAppBrowser.isAvailable()
.then(() => {
InAppBrowser.openAuth(url, redirectUri, {
// iOS Properties
dismissButtonStyle: 'cancel',
// Android Properties
showTitle: false,
enableUrlBarHiding: true,
enableDefaultShare: true,
})
.then((response) => {
// Only gets to this point if user explicitly cancels.
// So this does not trigger upon successful login.
})
// catch handlers follow
Using the above, my app correctly open up an in-app browser and I can login fine using a test user for my test app. Upon successful login though, I don't get redirected back to the .then completion handler. It just stays in the in-app browser view and I see the same message from Facebook that I see when logging in using a web browser. It says something like "Success. Please treat the url the same as you would a password", or something like that.
I may be missing something here, but I thought the purpose of passing redirectUri as an argument to openAuth was so that upon redirection to that URI, the completion handler would be triggered.
Question: How do I redirect back to the completion handler upon login success?
I think that you already have a solution but thought it might be useful for someone else facing this issue. If you don't have a solution so far follow my instructions:
You can't directly redirect back to your application using deep link, since Facebook will not call a link `like myapplicationname://mycustompath´. It's only possible to call links using the https-protocol (https://...).
The solution I'd suggest you to use is to redirect using your own API (Facebook -> Your API -> Deep Link Redirection). You will understand why this is required in the most of the real world applications at the end of the instructions.
Starting from your react-native app call the authorize endpoint of Facebook with a redirection to your application and set the global deeplink of your app as redirect uri.
InAppBrowser.close();
InAppBrowser.openAuth("https://graph.facebook.com/oauth/authorize?client_id=YOURCLIENTID&redirect_uri=https://YOURDOMAIN:PORT/auth/facebook", "{YOURAPPSDEEPLINKNAME}://{SOMEPATHYOUWANTTOEND}")
.then((response) => {
handleAuthorized(response, LOGINTYPE.FACEBOOK);
});
Now after login you'll be redirected to your API with the authorization code token as query parameter (e.g. https://YOURDOMAIN:PORT/auth/facebook?code=AVERYLONGCODESENTBYFACEBOOK)
Using this code token from the query parameter, you make another API Call to get the access_token for the user
{GET}: https://graph.facebook.com/v15.0/oauth/access_token?client_id=YOUR_CLIENT_ID&redirect_uri=https://YOURDOMAIN:PORT/auth/facebook&client_secret=YOUR_CLIENT_SECRET&code=AVERYLONGCODESENTBYFACEBOOK
Facebook's API will send you an answer as JSON with the access_token inside.
You can make another call using the access token of the user, to get the userId and the username
{GET}: https://graph.facebook.com/me?access_token=ACCESS_TOKEN_SENT_BY_FACEBOOK_IN_PREVIOUS_GET_REQUEST.
If you need the e-mail address for the user you have to make another call. Make sure you'd set the permission to read the e-mail address for your app on the developer portal of facebook.
The following request will return you the id, name and the email of the user
{GET}: https://graph.facebook.com/USERIDFROMPREVIOUSREQUEST?fields=id,name,email&access_token=ACCESSTOKEN
I think you want to save all these information to a database and create a session in order to keep the user logged in and therefore all the requests described will be useful for you in a real application.
After doing all the backend stuff, you're ready for the redirection using deep link. To do that, set a meta-tag to redirect the inappbrowser to your application:
<meta http-equiv="refresh" content="0; URL={YOURAPPSDEEPLINKNAME}://{SOMEPATHYOUWANTTOEND}" />