How can I add basic authentication to cy.origin? - cypress

The website used for user authentication is protected using Basic Authentication. How can I switch to a different baseUrl while still using Basic Authentication to view the page?

I think you are looking for the Cypress docs Add basic auth headers
Cypress will automatically apply the right authorization headers if you're attempting to visit an application that requires Basic Authentication.
Provide the username and password in the auth object. Then all subsequent requests matching the origin you're testing will have these attached at the network level.
cy.visit('https://www.acme.com/', {
auth: {
username: 'wile',
password: 'coyote',
},
})
so it's nothing to do with the cy.origin() per-se, rather the cy.visit() that takes you to that other origin.

Related

How to log in from a single page react app to Laravel 7.x on another domain?

I have a single page create-react-app running on localhost:3000 and I want to log in to a laravel 7.x instance running on myapp.loc (vhost).
Eventually I would like a single page running on app.mysite.com with laravel running on api.mysite.com.
I'm able to log in to my laravel instance directly from myapp.loc. I've installed Laravel passport and the scaffolding, etc and can create Client IDs and Secrets, but I'm unsure if they are needed and if so, how to use them.
What I am unsure of and cannot find any documentation for, is how to log in to laraval from my SPA (running on localhost:3000). I have set my CORS headers and can connect requests that don't require auth, but I don't know how to log in or structure auth requests once logged in.
I can't find any documentation on axios.post / get requests with a focus on logging in from another domain and maintain user-based access.
Since I don't know enough to ask a concise question, there are three layers that I feel like I should be searching for an answer.
Is it possible for laravel to act as the backend for my single page app from another domain?
If so, are there documented, best practices / well worn paths for accomplishing this?
If so, what would a sample axios login and subsequent session call look like? (e.g. payload and header shape)
Yes you can, I suggest to use https://laravel.com/docs/7.x/sanctum instead of passport because is easier to setup and it was created especially for this scenario.
You need to configure CORS using the official Laravel Package https://github.com/fruitcake/laravel-cors this way you will open Laravel's CORS to be able to reach it from anywhere localhost, or any domain that you can set up into allowed_origins. inside the cors.php config file according to the documentation of the package.
After configuring Sanctum/Passport and ensuring you are generating the required token for your SPA using the createToken method described in Sanctum or Passport docs, you have to save the token to connect to your protected endpoints however they recommend to use cookie SPA based authentication but that's not strictly necessary.
Create an API Service
In this example I will create an API Service to encapsulate API calls
import axios from 'axios';
const URI = 'https://yourlaravel.api/api/';
axios.defaults.headers.common = { Accept: 'application/json', 'Content-Type': 'application/json' };
const ApiInstance = axios.create();
const API = {
login: (user) => {
return ApiInstance.post(`${URI}login`, user);
},
getUser: () => {
return ApiInstance.get(`${URI}user`);
},
setUser: (user) => {
return ApiInstance.post(`${URI}user`, user);
},
};
Send A Login Request to your login endpoint and save the token
import API;
API.login({email:'mail#domain.com',password:'32323'})
.then(response=>{
//save the token
//response.data.accessToken
})
Fetch data from your protected endpoints using the token
//Set the default authorization header when you have an access token
axios.defaults.headers.common = {'Authorization': `Bearer ${token}`}
//Get your data
API.getUser().then(response=>{
//response.data
})
//Post something
API.setUser({user:'Os'}).then(response=>{
//response.data
})
All those things are possible, you just need to set up cors and you are good to go. For auth you can use passport or your own custom app key setup, it all depends on what you are trying to achieve. I suggest reading up about RESTfull apis, that would be a good start.
In order to perform a handshake between FE and BE on FE you would have a login form submission of which will send e request to BE (backend api) and if login is success you send back a key which then FE should store. Any future requests from FE should append that key in the header to gain access to authorised areas.
There should be plenty of information on this subject (RESTfull Api & Token authentication) on google.

Manually enter auth credentials in dialog

I need Cypress authenticate a XHR request done in my app. The authentication is not Basic, but Digest, which has made finding help harder.
There also seems to be a bug for authenticating requests, such like:
cy.request({
url: 'http://postman-echo.com/digest-auth',
auth: { user: 'postman', pass: 'password', sendImmediately: false },
})
https://github.com/cypress-io/cypress/issues/2128
I'm wondering if there is a temporary workaround involving making Cypress manually entering the credentials in the dialog?
I've looked into listening to events such as window:alert and window:confirm, but these don't seem to catch the auth dialog.
TL:DR: How can I make Cypress enter the credentials manually in the dialog?
cy.visit currently only supports sending Basic HTTP auth, but I've opened a feature request for digest auth: https://github.com/cypress-io/cypress/issues/4669
In the meantime, the workaround would be to disable HTTP authentication in your local testing environment.
I tried this
Try and send the username and password in the url
Send Auth Headers with your request
Worked fine in the below example
https://www.youtube.com/watch?v=0HAn6B4E-Kc
You would probably need something like this
cy.visit('http://yoururl/')
cy.get('input#username').type('username')
cy.get('input#password').type('password')
cy.get('button#signin').click()

Laravel-App with basic auth in URL: OK for web, not OK for API

I try to run Koel (or Kutr, a fork) behind a HTTP basic auth user/password dialog: https://koel:8001 behind an nginx reverse proxy https://koel with basic auth enabled.
Koel is a Laravel application and uses JWT tokens and the Authorization Header field like this:
Authorization: Bearer eyJ0eX…
This means that every request overwrites the basic auth header (that uses the same field) and the login dialog pops up again and again.
When I set APP_URL in the file .env to https://user:pass#koel, all links to static files are indeed properly fetched. API calls like https://koel/api/data unfortunately ignore this.
Can I use some kind of Middleware to inject user and password into the API calls?
The principle of JWT is to avoid using basic auth which is like no authentication at all (your credential are transmitted in clear with basic authentication).
So, I'd say that JWT using Authorization is a good thing, since it prevents your credential from leaking in case of bad configuration.
The principle of JWT is to, upon valid credential, to generate a token that's stored in your browser (a bit like a cookie). Then, with this token, there is no need to present any authentication whatsoever (so the credentials are present only once upon authentication, no during the complete session like for a basic or digest authentication).
With Koel, you can't do much to prevent this, since there is no way to change the login page in order to use basic authentication.
With Kutr however, I've done some hooking mechanism so you can plug your own login page (which can be as simple as validating a basic authentication).
It's available here
In your case, replace the top of the file from:
// Require your CMS bootstrap code here
require_once("yourCMSbootstrap.php");
// Bootstrap your CMS and check the session is valid (if not your CMS will fallback the login screen)
checkSession();
to
// Assert the user provided an basic or digest authentication
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit();
} else {
$login = $_SERVER['PHP_AUTH_USER'];
$pw = $_SERVER['PHP_AUTH_PW'];
if (!checkValidLoginAndPW($login, $pw))
{
header('HTTP/1.0 401 Unauthorized');
echo 'Game over';
exit();
}
}
Make sure the user's login also exists in Kutr's application

Implementing Incremental Authorization in passport.js with the Google API OAuth 2.0

I'm currently using passport.js with the strategy from passport-google-oauth20 and I want to implement incremental authorization. I'm using yarn as my package manager and my server also uses the Nodejs and Express packages.
When a user first logs in, all I need is their basic gmail information like their email address, so I specify the "email" scope:
router.get('/',
passport.authenticate('google', {
hd: '(my domain)',
scope: ['email'],
accessType: 'offline',
prompt: 'consent'
}));
When they are on a different page, they can request that the site send an email from their account so I now need to add the "https://www.mail.google.com" scope to the session. I couldn't find any documentation for it on the passport.js website and google has a page on it, but I don't think that's setup since I'm using passport.js. I've tried using
router.post('/sendEmail',
passport.authenticate('google', {
hd: '(my domain)',
scope: ['https://www.mail.google.com'],
accessType: 'offline',
include_granted_scopes: true,
prompt: 'consent',
failerRedirect: '/auth',
successRedirect: '/google/authorizedSendEmail'
}));
but I get the error
Failed to load https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent&hd=(myDomain)&response_type=code&redirect_uri=(myCallbackURL)&scope=https%3A%2F%2Fwww.mail.google.com%2Fauth%2Fuserinfo.email&client_id=(usersID).apps.googleusercontent.com:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '(my domain)' is therefore not allowed access.
when requesting it.
If I include "https://www.mail.google.com" in the initial scope and don't have the second authentication call, then it does work; an email is sent from the user's account. But, of course that isn't incremental authorization.
Also, I think incremental authorization should have its own tag, but I don't have the rep to do so, could someone add it please?

Private REST API with Ajax call

How to authorize only my app to use my REST API ?
I have this code in Javascript
$.ajax({
type: 'DELETE',
url : 'removeTest',
data: { ... },
beforeSend:function(){
...
},
complete:function(){
...
},
success:function(data, textStatus, jqXHR){
...
}
});
This call will remove a user from the database with REST API in PHP. The problem is that everyone can remove a user, with POSTMAN (Chrome plugin) for exemple. How can I protect my REST API to authorize only my app.
Check the HTTP_REFERER is not enough. What could be better ?
Thanks for your help
You have several possibilities here. In general you could authorize the user, the app or both. This depends on your application requirements.
Authenticate Applications
To authenticate the application you could use a token based system, such as an API-Key. This means any request would be signed using additional request parameters. Take a look at the way amazon does this in their S3 service for example. If your application will be the only one that will access the rest API you could alternatively simply restrict the acces by the IP address.
If there will be multiple users using the service via your client, you may also need to authorize the access to certain functions. For example: Will every user be allowed to delete any resource? If the answer is no, you have to implement
Authenticate and authorize users
A simple way to authenticate users in a RESTful API is using HTTP Basic or Digest Auth. In this setting the user credentials are sent via the Authorization header in a form of username:password as Base64 encoded hash to the server. Please note that you should only do this via an secured connection using HTTPS!
Additionally you could also take a look at more complex/sophisticated practices like OAuth.

Resources