IdentityServer4 .Net Core 3 TokenResponse Json is null - identityserver4

I have this code
class Program
{
static async Task Main(string[] args)
{
HttpClient Client = new HttpClient();
var disco = await Client.GetDiscoveryDocumentAsync("https://intranet.mycompany.com/");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// request token
var tokenResponse = await Client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "Console",
ClientSecret = "secret",
Scope = "dev_api",
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
}
}
We just setup intranet.mycompany.com on etc/host to serve local ip address and we setup a self signed certificate. GetDiscoveryDocumentAsync works fine, it connects to IdentityServer, but RequestClientCredentialsTokenAsync returns BAD REQUEST
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Cache-Control: no-cache
Pragma: no-cache
Transfer-Encoding: chunked
Server: Microsoft-IIS/10.0
Date: Mon, 14 Oct 2019 15:31:42 GMT
Expires: -1
}}
Any guidance on how setup identity server 4 .core 3 on local network by IP address with self signed certificate? I want to create a demo infrastructure outside localhost for our clients so we can later publish on the client site.

So I accidentally figured out how solve this scenario for local network with HTTPS. You must have 1 self signed certificate named localhost to use in identity server Startup class ConfigureServices
services.AddSigningCredential("CN=localhost");
Then you must have another self signed certificate named your.local.domain and setup that domain in etc/host file. You must also setup your web server with this certificate for identity server site.
If you want to connect to identity server from other machine in your local network you have to copy your your.local.domain and install it on that machine. Also you have to setup etc/host file on that machine to redirect to the web server.
Example of etc/host on both machines
192.168.0.1 your.local.domain
Your working ULR for identity server will be something like this
https://your.local.domain

Related

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

[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,
};

https://www.example.com and https://example.com socket.io connection problem

How do I get both https://example.com and https://www.example.com for endpoints to connect?
socket.io connect no problem at htttps://example.com
but not https://www.example.com
my server looks like
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: "https://www.example.com:3000",
methods: ["GET", "POST"],
allowedHeaders: ["my-custom-header"],
credentials: true
}});
and my client endpoint looks like this
const ENDPOINT = 'https://example.com/';
Configure your DNS to point both example.com and www.example.com to the same host IP address and then support both domains with your cors configuration. Then, either URL will go to the same host.
You can avoid cors entirely if you configure socket.io to start immediately with a webSocket, not with the http polling that it usually starts with. You can do that in the client by adding the transports: ['websocket'] option to the client connect.
And, your custom header was probably blocking CORs and requiring pre-flight permission. CORs has two levels, simple and not-simple. When you add a custom header, you force CORs into the not-simple route where it requires preflight which is probably more than the built-in socketio cors features will handle.

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!

How can I connect my Reactjs Web App to a secured MQTT broker?

I have a reactjs web application that needs to connect to a secured mqtt broker.
The web application is deployed in AWS elastic beanstalk and is using HTTPS. I already have a secured MQTT broker but when trying to connect my web app to the mqtt broker I have this error:
Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID
I am using MQTTjs package. I already have certificate for both server and client side.
Here's my code:
var client = mqtt.connect('wss://[ipaddress]:8884');
I also tried this code:
var mqtt = require('mqtt');
var fs = require('fs');
var path = require('path');
var CERT = fs.readFileSync(path.join(__dirname, './ca.crt'));
var KEY = fs.readFileSync(path.join(__dirname, './ca.key'));
var options = {
rejectUnauthorized: true,
port: 8884,
host: ipaddress,
protocol: 'mqtts',
ca: CERT,
keyPath: KEY,
certPath: CERT
}
But the error for this code is:
Uncaught TypeError: fs.readFileSync is not a function
You can't load certificates from disk in the browser. So the second version of the code just won't work.
You need to add both the CA and the Client certificate to your browsers certificate store.
The browser should then use the CA cert to validate the broker and should then pick the correct client cert to present to the broker as identification.

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