need to mock POST request in cypress - testing

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.

Related

Cypress Inserting an already authenticated token

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)
})
})
})

STRAPI: Using auth0 for API Authentication in Strapi works and works not

Node: v12.16.1
Strapi: 3.0.0-beta.20.1
I'm new to strapi and have to say at first: I love it!
I want to use Strapi in addition with Angular 9 and for authentication I want to use Auth0.
For my setup with Auth0 I followed the guide in the strapi documentation and customizied my permissons.js in the user-permissions plugin.
Well!
I have a very confusing issue.
When I get back my access_token from Auth0 I put it to my calls as bearer authentication token, the request to the strapi endpoint works mostly for the first time, maybe sometimes the second and many more calls also, and then it fails. If I wait a few seconds then it works again, and after a successful call also it fails again. (also testing outside my angular-app with postman - same behavior)
If I work with the strapi jwt, it works permanantly, independet from my frequency of requests.
This is confusing me since a couple of days.
My response from strapi if it fails is
Invalid token: Token did not match with Strapi and Auth0 as catched in the error part.
If it would never work I would have to say I'm doing something wrong, but it works sometimes, so I would say my way is technically correct. But of course I will do something wrong.
Has everybody else had an issue like that by authentication with Auth0 and strapi and understand what I try to explain?
Here is my snippet of code (I would say exactly copied from docu with a bit personalization):
...
try {
const data = await axios({
method: 'post',
url: 'https://my.eu.auth0.com/userinfo',
headers: {
Authorization: ctx.request.header.authorization
}
});
console.debug('data from Auth0: ' + JSON.stringify(data.data));
// if you want do more validation test
// feel free to add your code here.
return await next();
} catch (error) {
return handleErrors(ctx, new Error('Invalid token: Token did not match with Strapi and Auth0'), 'unauthorized');
}
...
I hope anybody had a similar issue and any idea how this can be fixed. Maybe it is an issue in Auth0.
You'll have to update url: 'https://my.eu.auth0.com/userinfo' replacing my.eu with your actual subdomain on auth0.

How do I get react-native-inappbrowser-reborn to trigger success upon successful Facebook login

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}" />

loopback protected routes/ensure login

How do I ensure that a user is logged in before I render a view using loopback?
I can loggin in the front end using my angular app. But I wanted to block anonymous users from viewing the page.
I thought it would be a header, something like headers.authorization_token, but it does not seem to be there.
I am looking for something like connect-ensurelogin for passport, without having to use passport.
This is the $interceptor that solves your problem.
This code detects 401 responses (user not logged in or the access token is expired) from Loopback REST server and redirect the user to the login page:
// Inside app config block
$httpProvider.interceptors.push(function($q, $location) {
return {
responseError: function(rejection) {
if (rejection.status == 401) {
$location.nextAfterLogin = $location.path();
$location.path('/login');
}
return $q.reject(rejection);
}
};
});
And this code will redirect to the requested page once the user is logged in
// In the Login controller
User.login($scope.credentials, function() {
var next = $location.nextAfterLogin || '/';
$location.nextAfterLogin = null;
$location.path(next);
});
Here is one possible approach that has worked for me (details may vary):
Design each of the Pages in your Single Page Angular App to make at one of your REST API calls when the Angular Route is resolved.
Secure all of your REST API Routes using the AccessToken/User/Role/ACL scheme that LoopBack provides.
When no valid Access Token is detected on the REST Server side, pass back a 401 Unauthorized Error.
On the Client Side Data Access, when you detect a 401 on your REST Call, redirect to your Logic Route.
For the smoothest User Experience, whenever you redirect to Login, store the Route the User wanted to access globally
(localStore, $RootScope, etc.) and redirect back there when the User
Logs in and gets a valid Access Token.
Here is the LoopBack Access Control sample: https://github.com/strongloop/loopback-example-access-control

Log in to my web from a chrome extension

I've got a web where logged in users can fill out a form to send information. I wanted my users to do this from a chrome extension too. I managed to get the form to sen information working but I only want to be logged in users to be able to do that. It's like a twitter or Springpad extension when the user first opens up the extension, it would have to log in or register. I saw the following answer at stack overflow: Login to website with chrome extension and get data from it
I gave it a try and put this code in background.html:
function login() {
$.ajax({
url: "http://localhost/login", type: "GET", dataType: "html", success: function() {
$.ajax({
url: "http://localhost/login", type: "POST", data: {
"email": "me#alberto-elias.com",
"password": "mypassword",
},
dataType: "html",
success: function(data) {
//now you can parse your report screen
}
});
}
});
}
In my popup.html I put the following code:
var bkg = chrome.extension.getBackgroundPage()
$(document).ready(function() {
$('#pageGaffe').val(bkg.getBgText());
bkg.login();
});
And on my server, which is in node.js, I've got a console.log that shows user information when he logs in, so I saw that when I load my extension, it does log in. The problem is how can I get the user to log in by itself, instead of manually putting my details in the code, how to stay logged in in the extension and when submitting the form, sending the user's details to the web.
I hope I've managed to explain myself correctly.
Thanks.
Before answering this question I would like to bring to your notice that you can make cross origin xhr from your content scripts as of Chrome 13 if you have declared proper permissions. Here is the extract from the page
Version note: As of Chrome 13, content scripts can make cross-origin requests to the same servers as the rest of the extension. Before Chrome 13, a content script couldn't directly make requests; instead, it had to send a message to its parent extension asking the extension to make a cross-origin request.
Coming to the point. You simply have to make an XmlHttpRequest to your domain from your extension (content script or background page) and wait for the response.
At Server
Read the request and session cookie. If session is valid send proper response, else send an error code. 401 or anything else.
At client
If response is proper display it, else display a login link directing to login page of your website.
How it works:
It will work if cookies in user's browser is enabled. Whenever user logs in to your website your server sets a session cookie which resides in user's browser. Now with every request that the browser sends to your domain, this cookie is transmitted. This cookie will be transmitted even if the request is from a Google Chrome Extension.
Caveat
Make sure you display proper cues to user indicating that they are logged in to your application. Since your UI will be mostly inside the extension, it is possible that user might not be aware of their valid session with your website and they might leave an active session unattended which might be abused if user is accessing it from a public internet kiosk.
Reference
You can take a look at a similar implementation that I have done with my extension here.
So the user logs into your server and you know that. I am a bit confused if you then want the user to be able to browse your website with those credentials or a third party website with those credentials.
If it is your website then you should be able to set a cookie that indicates whether they are logged in. Then detect this server side when they navigate your site.
If it is a third party site then the best you can do is create a content script that either fills out the login form and autosubmits for them or analyze the login post data and send it along yourself, then force a refresh.