axios bypass TLS certificates error. No solution at all for FRONT END (React) - reactjs

[EDIT] : one answer below
trying to call my node https backend from react using axios (of fetch)...
Always facing certificate errors for self signed ones. Can "by pass" the REJECT_UNAUTORIZED error by setting NODE_TLS_REJECT_UNAUTHORIZED=0 when starting the react app, but this is clearly not the solution. I'm also facing different messages such as ERR_CERT_COMMON_NAME_INVALID.
Worth to mention that everything is running fine when using postman, when activating the certificate verification and uploading my self signed CA PEM certificate in postman.
I tried to do the same in chrome (uploading the self signed CA PEM certificate in the root CA Store) but without luck. Still the warning...
Of course I tried the solution to add an https agent (see code below), but this is also not fixing the issue.
That solution seems to be fine for NODE apps, but not from Front End apps in React or any other language,..
So what is the solution ? How we can request a local self signed https backend server from react without these warnings ?
There are millions of issues like this reported here or in axios github, but still no viable solution ?
Thks
axios.defaults.httpsAgent = new https.Agent({
rejectUnauthorized: false,
port: 443,
});
const tokensData = await axios.post<TLoginApi, AxiosResponse<TLoginApi, any>, TLoginDTO>(
`${process.env.AUTH_URL}/login`,
credentials,
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
// or this but failed anyway httpsAgent: new https.Agent({
rejectUnauthorized: false,
port: 443,
}),
}
);

answering my own question.
To avoid using NODE_TLS_REJECT_UNAUTHORIZED=0, you need to create your self sign certificate.
The main issues I was facing was related to LOCALHOST and GOOGLE CHROME (at least release 108) , because my test backend is in same dev machine.
I came up with a solution to generate self signed certificate that is OK for Google Chrome with domain like 'localhost'.
When generating the server certificate, you need to add an extension file that is adding some DNS and the IP address (IP4 & IP6) of 'localhost'.
Also important to import your root CA in Chrome to avoid Reject unauthorized...
steps to create CA and certificates
1- generate your root CA. It has to be imported in Google Chrome
openssl req -x509 -newkey rsa:4096 -days 999 -keyout ca-key.pem -out ca-cert.pem -subj "/C=US/ST=Oregon/L=City/O=whatevev/OU=whatevev/CN=your name if you want/emailAddress=test#test.com"
2- generate the server private key and CSR
openssl req -newkey rsa:4096 -keyout server-key.pem -out server-req.pem -subj "/C=US/ST=Oregon/L=City/O=whatevev/OU=whatevev/CN=your name if you want/emailAddress=test#test.com"
3- finaly generate the server key, with some additional conf (see below)
openssl x509 -req -days 999 -in server-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile server-ext.conf
example of server-ext.conf file that prevent chrome from complaining about wrong DNS or anything else
can fine more information about this extension file HERE
subjectAltName=DNS:test.home,DNS:localhost.home,DNS:localhost,IP:0.0.0.0,IP:127.0.0.1,IP:::1
Finally, on your BACKEND, you can configure your nodejs express server using your root CA and the certificate/key :
const httpsOptions: https.ServerOptions = {
key: fs.readFileSync(
path.join(__dirname, "auth", "certificats", "server-key.pem")
),
cert: fs.readFileSync(
path.join(__dirname, "auth", "certificats", "server-cert.pem")
),
ca: fs.readFileSync(
path.join(__dirname, "auth", "certificats", "ca-cert.pem")
),
passphrase: "passpahrase of root CA,
};

Related

Vite Proxy Using Http2 returns "unable to verify the first certificate"

I am trying to setup a development environment using HTTP2 requests with Vite and a local backend. I proxy requests between client and server both running on localhost. The server is configured with a generated certificate using Mkcert (https://github.com/FiloSottile/mkcert).
Configuration:
To configure the certificate I run the following steps:
mkcert.exe -pkcs12 "test" "127.0.0.1" "localhost"
Configure the backend to use the generated cert
mkcert.exe -install
Use the following vite configuration:
server: {
https: {
pfx: fs.readFileSync(`./cert/test.pfx`),
passphrase: "some-phrase",
},
proxy: {
"/api": {
target: `https://localhost:15000`,
changeOrigin: true,
secure: true,
},
},
},
Currently this configuration works on Mac but fails on Windows. Both use the same backend and certificates but on Windows the proxy will fail with: "unable to verify the first certificate". In the browser the certificate chain is a correctly validated.
Troubleshooting:
When I run openssl s_client -showcerts -connect localhost:15000 -servername localhost the query results in "unable to verify the first certificate" as well basically indicating that the mkcert Root CA isn't properly found.
Using openssl s_client -showcerts -CAfile "C:\Users\SomeUser\AppData\Local\mkcert\rootCA.pem" -connect localhost:15000 -servername localhost the certificates will be correctly validated. This should indicate that the proxy lacks a reference to the rootCA of Mkcert.
I have tried supplying the rootCA using the following script in package.json:
"dev": "cross-env NODE_EXTRA_CA_CERTS=\"C:\\Users\\SomeUser\\AppData\\Local\\mkcert\\rootCA.pem\" && vite",
This doesn't help however and the proxy still fails with the same error. I assume Mac and Windows work differently when supplying the CA root certs to Node or fails silently?
Anyway, how can this solved? I have been thinking about supplying the full cert chain in a cert that I supply to Vite, but this shouldn't be needed if Node could just reference the RootCA correctly.

Certificate for AddSigningCredential

I am new in IdentityServer4 and trying to create JSON Web token. For development I have used AddDeveloperSigningCredential but for other environments I need to use AddSigningCredential but I do not know how to get certificate. I don't have any certificate actually and not sure how can I generate it? Can anyone provide some inputs how to generate certificate and use with AddSigningCredential and then after creation of token, how can I validate token using same certificate in API
After generation of certificate as described in comments. I have added following code
Code in identity server
services.AddIdentityServer() .AddAspNetIdentity() .AddConfigurationStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, opt => opt.MigrationsAssembly(migrationAssembly)); }) .AddOperationalStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, opt => opt.MigrationsAssembly(migrationAssembly)); }) .AddSigningCredential(certificate);
Code in API
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "localhost:44339"; });
Issuer is
{"issuer":"localhost:44339","jwks_uri":"https://…" I have fetched this information from
https://localhost:44339/.well-known/openid-configuration
I am getting 401 error in postman and getting error is WWW-Authenticate: Bearer error="invalid_token", error_description="The audience 'policyservice' is invalid"
You can use any tool that can generate a private/public key pair.
In the example below I use openssl.
First we create a RSA private key:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes256 -out rsa-private-key.pem
Then you can create a certificate using :
openssl req -new -x509 -key rsa-private-key.pem -days 365 -subj "/CN=MyRSACert" -out rsa-cert.crt
Then you can package up the cert and private key into a .pfx file:
openssl pkcs12 -export -inkey rsa-private-key.pem -in rsa-cert.crt -out rsa.pfx
Then in code you can load the cert using:
var rsaCert = new X509Certificate2("rs256.pfx", "yourpassword");
Then to add it to IdentityServer you can use:
services.AddIdentityServer()
...
.AddSigningCredential(rsaCert)

create-react-app | Is it possible to serve a file from backend instead of serving index.html when a browser connect to app

I have been trying to enable SSL on my MERN heroku-deployed app.
I have been stuck at this step far more than necessary:
I am following this tutorial to set-up SSL certificate on my website.
After, generating the certificate using this command locally:
sudo certbot certonly --manual
I was asked to do this by the terminal:
Create a file containing just this data:
dC9Ry5Ps_qgkOheuWnxCXFobim8vshqMqbDC9FQS4ic.noFTXhkC3HFnZ-RC9djrM6FpWGRy2AFSB17xz59apDA
And make it available on your web server at this URL:
http://www.site_name.com/.well-known/acme-challenge/dC9Ry5Ps_qgkOheuWnxCXFobim8vshqMqbDC9FQS4ic
According to the tutorial, I had to do this on the backend:
app.get('/.well-known/acme-challenge/:content', function(req, res) {
res.send('xxxxxxxxxxxx-yyyy.zzzzzzzzzzzzzzzzzzz')
})
I did that, and as expected it did not work since the certbot will be targeting the front-end and not the backend according to this:
Create a file containing just this data:
dC9Ry5Ps_qgkOheuWnxCXFobim8vshqMqbDC9FQS4ic.noFTXhkC3HFnZ-RC9djrM6FpWGRy2AFSB17xz59apDA
And make it available on your web server at this URL:
http://www.site_name.com/.well-known/acme-challenge/dC9Ry5Ps_qgkOheuWnxCXFobim8vshqMqbDC9FQS4ic
And it just doesn't make sense for me, to make this data available
dC9Ry5Ps_qgkOheuWnxCXFobim8vshqMqbDC9FQS4ic.noFTXhkC3HFnZ-RC9djrM6FpWGRy2AFSB17xz59apDA
on the client side.
So my question is: Should it be available on the client side or the server side? It it's on the server side, should I just write code on the client side that would communicate with the server in order to retrieve it?
So when the verification process happens and it tries to access the backend endpoint through this:
http://www.site_name.com/.well-known/acme-challenge/dC9Ry5Ps_qgkOheuWnxCXFobim8vshqMqbDC9FQS4ic
The app treats it as it's trying to access client-side file...
In other words, I am unable to make it target the backend endpoint.
All tutorials are ignoring this point and it makes me feel like I'm missing something or that I'm stupid.
So any idea what I should do?
This guy seems to be having the same problem as me but no answer was provided.
This is what I do for my own MERN Stack application and it works, furthermore I always deploy my projects on heroku.
First thing that I do I first generate my SSL Cert and Key using:--> openssl
This is the command you would want to run: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
This command will generate you two files cert.perm and key.pem
Now it time to setup your server's https
const app = require('express')();
const https = require('https');
const fs = require('fs');
//GET home route
app.get('/', (req, res) => {
res.send('Hello World');
});
// we will pass our 'app' to 'https' server
https.createServer({
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem'),
}, app).listen(3000);
Noting the with convention you can also have an http server hooked up by doing the same as http.createServer and all you do is pass down the express app.
However if the are certain things that you want to restrict for http then you can separate the two from not using the same express app I hope this makes sense
I found a solution: Serving a static file from backend.
Create a file in the root folder (root_folder/sec/ssl) containing the code :
2. Add this to your express router:
// Server static assets if in production
// Check if we are in production
if (process.env.NODE_ENV === "production") {
// Set static folder
app.use(express.static("client/build"));
// We want to get anything that is not one of those api routes
/********************************** */
/***Adding the certbot verification code */
/********************************** */
app.get(
"/.well-known/acme-challenge/url_code",
(req, res) => {
console.log(
"get(/.well-known/acme-challenge/ur_code)"
);
// __dirname is the current directory name
//We will tell the server if none of those routes are being hit then look into the build folder index.html
res.sendFile(path.resolve(__dirname, "sec", "ssl"));
}
);
/*********************************** */
app.get("*", (req, res) => {
// __dirname is the current directory name
//We will tell the server if none of those routes are being hit then look into the build folder index.html
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
That's it!

Starting a react app in HTTPS instead of HTTP

I want to know how to start a react app which was made using the create-react-app command in https instead of HTTP?
Use something like Root SSL certificate
Generate a key
openssl genrsa -des3 -out rootCA.key 2048
With they key you can generate a certificate which is good for 1,024 days
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
Open keychain access on your Mac and go to the certificates category and emport that rootCA.pem generated from the last step. Double click and under "When using this certiciate" select 'Always Trust'
Create an OpenSSL configuration file
server.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[dn]
C=US
ST=RandomState
L=RandomCity
O=RandomOrganization
OU=RandomOrganizationUnit
emailAddress=hello#example.com
CN = localhost
Create a v3.ext file to create a X509 v3 certificate.
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = #alt_names
[alt_names]
DNS.1 = localhost
Create a certificate key for localhost using the configuration settings stored in server.csr.cnf. This key is stored in server.key.
openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <( cat server.csr.cnf )
A certificate signing request is issued via the root SSL certificate we created earlier to create a domain certificate for localhost. The output is a certificate file called server.crt.
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext
You’re now ready to secure your localhost with HTTPS. Move the server.key and server.crt files to an accessible location on your server and include them when starting your server.
In an Express app written in Node.js, here’s how you would do it. Make sure you do this only for your local environment. Do not use this in production.
var path = require('path')
var fs = require('fs')
var express = require('express')
var https = require('https')
var certOptions = {
key: fs.readFileSync(path.resolve('build/cert/server.key')),
cert: fs.readFileSync(path.resolve('build/cert/server.crt'))
}
var app = express()
var server = https.createServer(certOptions, app).listen(443)
Check https://github.com/dakshshah96/local-cert-generator/ for more detailed instructions
Adding on to the solution provided by Mark, to import the certificate you must click File > Import Items. Then, search for the certificate on your hard drive. After doing so you can proceed with the remaining steps.

create-react-app proxy request fails with 404 when backend is hosted on Azure

I am having a bit of trouble setting up my create-react-app application to proxy requests to my test hosting on Microsoft azure. I have set up the proxy in my app's package.json as follows:
"proxy":{
"/api/*":{
"target":"https://mytestbackend.azurewebsites.net",
"secure":false
}
}
I have set up an axios request to be sent to the backend server on azure. It is in a stand-alone .js which I call from one of my react application's events. It looks like this:
import axios from 'axios';
const login = async (username, password) => {
console.log("Username to send is:"+username);
console.log("password to send is:"+password);
let response = await axios.post('/api/user/login', {username:username,password:password});
console.log(response);
};
export {login};
The problem can't be in my react components, because those two console.log() call show that the values entered are being recieved. If I remove the "secure":false setting from package.json, request fails with Http Error: 500. But if I use the secure setting, it fails with a 404 page. Can someone please shed a little light on what am I doing wrong? Can I only use the proxy on "localhost"? The documentation suggests otherwise. Any help is greatly appreciated.
I have verified that CORS is enabled for the domain on which the dev server is running on the Azure Management Portal. And if I do the request by using the backend's URL directly (that is, not using the create-react-app proxy), it works. The problem must be something in the way the proxy is configured.
The response text for the HTTP Errpr 500 which happens when not using secure is :
Proxy error: Could not proxy request /api/user/login from localhost:3000 to https://mytestbackend.azurewebsites.net (undefined).
Additional info: I have also tested by running my Backend locally on my development machine. The error message occurs but the "undefined" in the parenthesis says "UNABLE_TO_VERIFY_LEAF_SIGNATURE". If using "secure: false, I can call the login endpoint successfully, but calls to other endpoints which require authentication fail because the cookie is not sent by axios.
Doing:
curl -v https://mytestbackend.azurewebsites.net/api/user/login
Has this output:
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection #0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
create-react-app use WebPackDevServer which uses https://github.com/chimurai/http-proxy-middleware#options
So you can use all the options from the same
Now one key header that is import in such cases of externally hosted server is host. This at times can issues if not correct, see below example
Websocket works on EC2 url but not on ElasticBeanstalk URL
Next is the cookies might be associated with localhost, i checked and they should go without any modification. But you might want to use the cookieDomainRewrite: "" option as well
So the final config I would use is below
"proxy":{
"/api/*":{
"target":"https://mytestbackend.azurewebsites.net",
"secure":false,
"headers": {
"host": "mytestbackend.azurewebsites.net"
},
"cookieDomainRewrite": ""
}
}
Also on your client you want to use the withCredentials:true
let userinfo =await axios.get('/api/secured/userinfo',{withCredentials:true});
Create react app http-proxy-middleware, and should support the full set of options.
Some things I would try:
The path to match may be /api/** instead of /api/* if you want to nest multiple levels deep (eg. for /api/user/login)
You may need to add changeOrigin: true if you're proxying to something remotely (not on localhost)
You will likely want to keep secure: false as you aren't running localhost with https.
So in total, I would try
"proxy":{
"/api/**": {
"target": "https://mytestbackend.azurewebsites.net",
"secure": false,
"changeOrigin": true
}
}
After days of trying unsuccessfully to do this, I finally found a setup that works. Proxy is configured like this:
"proxy": {
"/api/user/login": {
"target": "https://localhost:44396",
"logLevel": "debug",
"secure": false
},
"/api/secured/userinfo": {
"target": "https://localhost:44396",
"secure": false,
"logLevel":"debug",
"secure":false
}
Request to both endpoints on the client have withCredientials:true
try {
await axios({
method:'post',
url:'/api/user/login',
withCredentials:true,
data:
{username:username,password:password}}
);
let userinfo =await axios.get('/api/secured/userinfo',{withCredentials:true});
return userinfo;
As you can see, I've moved to testing on my local dev machine. For whatever reason, this setup refuses to work on the azure-hosted backend. I would have preferred that it work as I originally intended, but at least now I can continue with my project.

Resources