Client side certificate javascript request - javascript

We're developing a react app with a python flask backend. Normally it all works fine, but when placing it behind a server with client side certificate requirement it almost works. It works fine in Chrome, not in Firefox.
The certificate is sent when entering the URL in the browser, it's not sent when making request from react.
The main request finishes fine, the page is displayed.
When loading the page makes a request to the backend, /backend/version.
That request fails, with nginx saying
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.10.3</center>
</body>
</html>
When I open devtools and paste the same url, it works fine. The client side certificate is sent by the browser.
How we make the request:
const fetchVersion = () => (dispatch, getState) => {
return dispatch({
[CALL_API]: {
endpoint: `${API_ROOT}/version`,
method: 'GET',
headers: {
"Authorization": authHeader(),
},
types: [FETCH_VERSION_REQUEST,
{
type: FETCH_VERSION_SUCCESS,
payload: (action, state, res) => {
const contentType = res.headers.get('Content-Type');
if (contentType && ~contentType.indexOf('json')) {
return res.json().then(json => json.response);
}
},
},
{
type: FETCH_VERSION_FAILURE,
meta: (action, state, res) => checkIfInvalidToken(action, state, res, dispatch),
}
],
},
});
};
What's missing? Why doesn't Firefox attach the certificate to the request like Chrome does?

You might try to see if the problem is resolved by explicitly specify [CALL_API].credentials value to include
According to the documentation
the default value is omit but firefox need include always send cookies, even for cross-origin calls.
Regarding the example in your question, the code could become something like:
[CALL_API]: {
endpoint: `${API_ROOT}/version`,
credentials: 'include',
method: 'GET',
headers: {
"Authorization": authHeader(),
},
...and so on
In a laboratory with purely experimental purpose I think I have reproduced a similar behavior you reported both with Chrome and Firefox and in this lab the credentials: 'include' solves the problem: video available here.

Related

Authorization header is missing on redirect URL to the same baseURL using axios on React Native

I am requesting a URL of API service which responds with status code 307 (redirect) on success and a URL location in response Headers. This is protected API and needs Authorization header for it to be fulfilled successfully. The problem I am facing is that the Authorization header is not appended to the redirected URL, it is only appended to the original call, and therefore the redirected URL call fails. This works perfectly fine in Postman but is not working with Axios. I have tried it in ReactJS web app and React Native mobile app (I want it to work on React Native mobile app actually) with different versions of axios (i-e v0.19.2, and v0.27.2) and the result is the same. This can be duplicate of this but it is working fine with RN v0.62 and the problem has started only after upgrade to v0.67.4, and also, the URL to which it redirects is also from the same base URL so there shouldn't be any security concerns. I am wondering if there is a solution or patch to this needed to be applied after the upgrade.
A sample code snippet is below:
const axiosInstance = axios.create({ headers: { Authorization: token } });
const data = JSON.stringify({
// ...data
});
var config = {
method: "put",
url:
`https://${baseUrl}/coupons/apply?calculateTotal=true&customerId=10398&validateCoupon=true&validateProducts=true`,
headers: {
Authorization: "token",
language: "EN",
"Content-Type": "application/json",
},
data: data
};
axiosInstance(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log("ERRRRRR", JSON.stringify(error));
});
I expect that auth header should be appended to the redirected URL as well because it belongs to the same base URL. The redirected URL looks like https://{$baseUrl}/api/v1/cart?calculateTotal=true&customerId=10398&validateCoupon=true&validateProducts=true
The environment setup looks like:
Axios Version [0.19.2, ~0.27.2]
Adapter [HTTP]
Browser [JavascriptCore (JS engine in Safari)]
Node.js Version [14]
OS: [iOS 15.6.1, OSX 12.4]
Additional Library Versions [React 17.0.2, React 18.2.0, React Native 0.67.4]

How to allow CORS in ASP.NET WebAPI

I'm trying to do an ajax post request from my web app to my ASP.NET endpoint (everything locally) to insert data in my database but I'm always getting POST http://localhost:44326/radar/insertConfig net::ERR_CONNECTION_RESET.
What I've gattered so far: The endpoint works perfectly when called via Insomnia;
I have other GET requests working perfectly in the same conditions (same page, same domains, etc);
I have tested my POST request to a public endpoint and everything worked fine; The request never leaves the browser (or never reaches the destination). The issue only happens when sending a POST request to my API from my web application.
What I've tried: using another browser (Chrome, Edge, Firefox), deleting cookies, clearing cache, reseting my connections, reseting my computer, trying from a different connection.
Note1: I'm using a VPN and an antivirus that I cannot turn off (neither).
Note2: When done via firefox the error becomes: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:44326/radar/insertConfig. (Reason: CORS request did not succeed).
My request:
var json = JSON.stringify(obj);
var url = "http://localhost:44326/radar/insertConfig";
$.post(url, json, function (status) {
if (status == "success")
alert("yay");
else
alert("nay");
}, "json");
The line at which the error happens: plugins.bundle.js:9838
xhr.send( options.hasContent && options.data || null );
What could be the possible problem?
Update: I added a middleware in my server that looks like this:
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
await next.Invoke();
});
But if I do that, then the return becomes 415 (unsupported media type). I tried to fix that by changing my request:
$.ajax({
url: url,
type: 'post',
data: json,
contentType: 'application/json',
dataType: 'json',
success: function (status) {
if (status == "success")
alert("yay");
else
alert("nay");
}
});
But then again, the CORS error returns...
from the error message it looks like your client and your server are from different domains, in order to be able to call your server from a different domain you need to enable CORS on your server.
for a asp.net server you can use Microsoft.AspNet.WebApi.Cors nuget package and add EnableCors attribute on your controller as follow
[EnableCors(origins: "http://{your-client-domain}", headers: "*", methods: "*")]
I had to change some stuff for this to work, here was my solution:
step 1: add this to the ConfigureServices and Configure (by default they're in the Startup.cs)
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
builder =>
{
//This is how you tell your app to allow cors
builder.WithOrigins("*")
.WithMethods("POST", "DELETE", "GET")
.AllowAnyHeader();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("MyPolicy");
}
step 2: Change the request to $.ajax instead of $.post
$.ajax({
url: url,
type: "post",
crossDomain: true,
data: json,
contentType: "application/json",
dataType: "json"
});

How do I fetch cookie data with React?

I have a MERN + Passport.js application that is using fetch to make a call to my express API in order to retrieve data stored in a cookie. I have my React frontend routed to localhost:3000, and my backend routed to localhost:3001. My cookies are not being saved, and thus my session is not persisting within my browser. It is not an issue with the express-sessions or passport middleware, as when I use POSTMAN to execute the necessary calls to my API, the cookie is stored and the session persists.
It is only when I attempt to pass this information through/to my front end that things go wrong. I have been stumped for a while and can't seem to find an answer anywhere.
This is the line that I am using to save the cookie:
handleLogin(event) {
event.preventDefault();
fetch("http://localhost:3001/users/login", {
// credentials: 'include',
credentials: 'same-origin',
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
})
// .then( (response) => response.json())
.then( (response )=> {
if(response.message){
alert(response.message);
}
})
Which correctly calls my API, which logs the current session, user data, and cookie.
Upon refreshing and making another request, I lose the cookie (it was never properly stored in the first place I think), and all session data.
This is the get request that I make whenever I navigate to a new page:
componentDidMount(){
var current_user = "";
fetch("http://localhost:3001/users/", {
// credentials: 'include',
credentials: 'same-origin',
method: "get",
headers: {
'Accept':'application/json',
'Content-Type': 'application/json'
}
})
// .then( (response)=> response.json())
.then( (response)=> {
if(response.user){
current_user = response.user;
this.setState({
user: current_user
}), ()=> console.log(response);
}
})
}
In response, I get an undefined user and no other response, and the cookie is never stored in my browser. But again, if I do this in POSTMAN, strictly doing the POST request followed by that GET request, the proper user data is returned and the cookie is shown in POSTMAN as well.
Any idea as to why fetch is not passing the cookie information back to my front end? I have tried both credentials: 'include' and credentials: same-origin.
Thank you!
It seems like the problem, or at least part of it, is your use of same-origin. From Mozilla docs (italics my own):
omit: Never send cookies.
same-origin: Send user credentials (cookies, basic http auth, etc..) if the URL is on the same origin as the calling script. This is the default value.
include: Always send user credentials (cookies, basic http auth, etc..), even for cross-origin calls.
The definition of "same origin" does not include different ports.
If you change same-origin to include, fetch should start managing your cookies correctly.
If not - do you know for sure that the cookie is "never stored in the browser"? With Chrome, you can check chrome://settings/siteData.

Access-Control-Allow-Origin Error Workaround - No Access to Server

I'm getting the following error using AJAX to call an API on UPS
XMLHttpRequest cannot load https://wwwcie.ups.com/rest/Ship. Response to
preflight request doesn't pass access control check: No 'Access-Control-Allow-
Origin' header is present on the requested resource. Origin
'http://localhost:63786' is therefore not allowed access.
AJAX Call:
$.ajax({
url: "https://wwwcie.ups.com/rest/Ship",
type: "POST",
dataType: 'json',
crossDomain: true,
contentType: 'application/json',
data: JSON.stringify(message),
success: function (result) {
//code to execute on success
}
error: function (result) {
//code to execute on error
}
})
I have no control over the API server so I cannot modify the headers being sent back. I've tried JSONP, changing the headers I send, and a number of other solutions to no avail. I've read that making a server-side proxy could be a possible fit but I'm not sure how I would go about this. Any advice/code samples on possible solutions would be greatly appreciated. Thanks in advance.
What is to stop a malicious website from sending requests to your bank's web app and transferring all of your money? To prevent these types of shenanigans, if you're using a web browser, the server must explicitly state which remote origins are allowed to access a certain resource, if any.
If you need a key to access it, then CORS probably isn't enabled. You can always double check by looking at the response headers. See this:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
So as others have already mentioned, you can get around this by making the request from your own server (where the headers don't identify it as a browser and subject to CORS limitations), and proxy it to your client side app.
Assuming you're using Node/Express, something like this should work:
const express = require('express');
const app = express();
const myHeaders = new Headers();
const myInit = { method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default' };
app.get('/ups/stuff/stuff', (req, res) => {
fetch('/ups/api/stuff', myInit)
.then(response => response.json())
.then(json => res.json(json);
});
app.listen(3000);
The native fetch API is neat because you can use it on both client and server, and it supports promises like jQuery.
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

React native ios http featch method fail

How to access the react native ios http featch
fetch('http://10.0.2.2:53396/index.cfm/?fwreinit=1', {
method: 'POST',
headers: { 'Accept': 'application/json','Content-Type': 'application/json',},
body: JSON.stringify({ public_key: "Arunkumar" ,private_key: "cfarun",})
}).then((response) => response.json())
.then((responseData) => {
console.log(responseData);
})
You can fix this issue by 1. using https link for your endpoints 2. Update info.plist file and add 'App Transport Security Settings' -> 'Allow Arbitrary Loads' and set its value to 'YES'.
See attached
Answered here
By default, iOS will block any request that's not encrypted using SSL. If you need to fetch from a cleartext URL (one that begins with http) you will first need to add an App Transport Security exception.

Categories