Checking in nodeJS Express if IIS Client Cert Authentication went ok - node.js

I have some client machines that connect to my IIS server using client certificate authorization. I have a node.js webapp published on IIS (using iisnode) that receives the requests from the clients.
Is there a way in express to know if the authentication was ok? I know that if the authentication was not ok the http request does not reach to express because it is rejected but I need a field to check if it was ok, because I access the same webapp without ssl and I this particar request only needs to be called if the call was using the client ssl authentication.
I tried with req.socket.authorized but it is undefined and with req.socket.getPeerCertificate(true) I am getting TypeError: req.socket.getPeerCertificate is not a function

req.secure
A Boolean property that is true if a TLS connection is
established. Equivalent to:
'https' == req.protocol;
http://expressjs.com/en/api.html#req.secure

Related

/signin-oidc redirect not working openid connect with Keycloak

Note -The issue is in my production app which has SSL installed. I have an ASP.NET Core MVC application deployed on Azure web app.
The authentication is done through Keycloak, using OpenID connect. Below is my Startup.cs code.
AddOpenIdConnect(options =>
{
// URL of the Keycloak server
options.Authority = "{keycloak realm url}";
// Client configured in the Keycloak
options.ClientId = "{clientid}";
// For testing we disable https (should be true for production)
options.RequireHttpsMetadata = true;
options.SaveTokens = true;
options.MetadataAddress = "{keycloak metadata url}"
// Client secret shared with Keycloak
options.ClientSecret = "{clientsecret}"
options.GetClaimsFromUserInfoEndpoint = true;
// OpenID flow to use
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
});
Once the authentication is done , Redirect URL https://{webapp}/signin-oidc gives below Error.
Also it says the "CONNECTION TO THIS SITE IS NOT SECURE" when the url is still https.
THE ISSUE IS INTERMITTENT. AT TIMES IT WILL REDIRECT BACK TO THE APPLICATION PROPERLY.
500 ERROR. WEB APP CANNOT HANDLE THIS REQUEST
One Pattern observed
For the first time if i open the application in Incognito mode, user is redirected to the application properly. and for the second time if i open in the normal tab, it will redirect properly. NO ISSUES
For the first time if I open the application in normal tab, it gives error.
Then the subsequent requests in incognito or normal tabs will throw the mentioned error too.
Could it be that the web-app is hosted in Azure has it HTTPS traffic terminated in the cloud infrastructure and then decrypts the traffic and sends it using HTTP to your application.
Meaning that your application only sees HTTP traffic while users on the public internet sees HTTPS traffic?
I think this is the default that HTTPS is terminated in Aure when you use their HTTPS infrastructure.
It was an error in the code . Changed response type from CodeIdToken to IdToken

Testing https API endpoint on localhost against a 3rd party app

I'm completely new to https and testing on localhost - please help!
What I'm doing
I'm trying to have a 3rd party app call my REST API endpoint, https://localhost:4000/test. This 3rd party only supports connections via https.
Problem
I'm trying to test this integration on localhost. To do this, I created a self-signed certificate using openssl. When this 3rd party service calls my endpoint, https://localhost:4000/test, it returns an error "Operation failed: Service is unreachable". On the docs, the error description is:
Error 400301: May be returned when executing Extensions, and the extension point did not respond.
Theory 1: The 3rd party doesn't trust my self-signed certificate (vs one from letsencrypt).
Clue #1: postman also didn't like my certificate and gave this error. I could only get it working by disabling the "SSL Validation" flag in Postman.
Clue #2: I put logs when my /test endpoint gets called on my server. But when the 3rd party triggers the event to call my API, nothing gets logged. It seems like they can't even start a connection to my server.
Theory 2: It doesn't support connections to localhost, only to endpoints deployed on the internet. Not sure how to test this theory other than actually deploying my API on the internet.
node.js snippet
https
.createServer(
{
key: fs.readFileSync("./my-private.key"),
cert: fs.readFileSync("./my-certificate.crt"),
passphrase: "secret",
},
app
)
.listen(PORT, () => {
console.log(`Listening on https://${HOST}:${PORT}`);
});
Command I used to sign my certificate:
openssl x509 -in my-request.csr -signkey my-private.key -out my-certificate.crt
One thing I noticed is that when I connect to https://localhost:4000 from Firefox, it tells me I'm not connected securely. Is this because the certificate is not signed by a 3rd party that the browser recognizes? How do I make this error go away?

Client Certificate Authentication between IIS reverse proxy and origin server

I am fairly new to IIS. We have a requirement to setup a reverse proxy using IIS. Now the origin / backend server can be on HTTP or HTTPS.
We have successfully configured mutual authentication on IIS for the client to require SSL and present a client certificate.
What we now need is to enable mutual authentication between the IIS reverse proxy and the origin server. Something like below:
---------- --------------------- ----------
| Client | --- HTTPS ---> | IIS Reverse Proxy | --- HTTPS ---> | Origin |
---------- --------------------- ----------
This setup requirement is similar to this question except that the question uses Apache HTTPD. Is this setup possible with IIS?
Any reference link / documentation would be appreciated.
I'm afraid it is impossible to do mutual authentication between native ARR and origin Server without client. Because we can't find kind of ProxyCertificate Property.
You could only set IIS ARR proxy by following this link. Then client will be able to send client certificate to ARR Server and ARR will add a request header X-ARR-ClientCert to deliver client certificate to your Origin Server.
https://blogs.msdn.microsoft.com/benjaminperkins/2014/06/02/configure-application-request-routing-arr-with-client-certificates/
Then Your origin server could process authentication by handling X-ARR-ClientCert header or asp.net HttpRequest.ClientCertificate property.
https://learn.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth

Invalid state on azure, but working locally

I have an Azure Active Directory tenant that I wish to authenticate with from my Node.js application running on an Azure App Service instance. I'm using passportjs and passport-azure-ad to do this.
Locally everything works fine. I can authenticate with the Azure AD tenant and it returns back to my page correctly. However on Azure it fails with the error:
authentication failed due to: In collectInfoFromReq: invalid state received in the request
My configuration is exactly the same (apart from redirectUrl) as I'm using the same tenant for local testing as well as in Azure yet it still fails. I've set up the proper reply urls and the authentication returns back to my application.
Here is my config:
{
identityMetadata: `https://login.microsoftonline.com/${tenantId}/.well-known/openid-configuration`,
clientID: `${clientId}`,
responseType: 'id_token',
responseMode: 'form_post',
redirectUrl: 'https://localhost:3000/auth/oidc/return',
allowHttpForRedirectUrl: false,
scope: [ 'openid' ],
isB2C: false,
passReqToCallback: true,
loggingLevel: 'info'
}
I'm using the OIDCStrategy.
My authentication middleware:
passport.authenticate('azuread-openidconnect', {
response: res,
failureRedirect: '/auth/error',
customState: '/'
});
I've compared the encoded state on the authorizerequest vs the returned response and they differ in the same way locally as well as on Azure, yet Azure is the only one complaining. Examples of how the states differ:
Azure:
Request state: CUSTOMEwAuZcY7VypgbKQlwlUHwyO18lnzaYGt%20
Response state: CUSTOMEwAuZcY7VypgbKQlwlUHwyO18lnzaYGt
localhost:
Request state: CUSTOMTAYOz2pBQt332oKkJDGqRKs_wAo90Pny%2F
Response state: CUSTOMTAYOz2pBQt332oKkJDGqRKs_wAo90Pny/
I've also tried removing customState completely yet it still fails.
Anyone know what's going on here? Am I configuring it incorrectly?
Edit: It appears that this may not be an issue with passport-azure-ad. I'm not sure yet, but some debugging revealed that there is no set-cookie header on the login request to my app. The session is created, but no cookie is set thus the returning response is unable to look up the session info including the state and compare them. The result is that it reports invalid state since it's unable to retrieve data from the session.
Turns out the problem was that the session was never properly created thus there was no state for process-azure-ad to compare. The reason for this was that I had configured express-session to use secure session cookies under the assumption that since I was connecting through the https://...azurewebsites.net address the connection was secure. This is not technically the case though.
Azure runs a load balancer in front of the Web Application effectively proxying connections from the outside to my app. This proxy is where the secure connection is terminated and then traffic is routed unencrypted to my application.
Browser -(HTTPS)> Load balancer -(HTTP)> Application
The result is that node did not report the connection as secure unless a set the configuration option trust proxy:
app.set('trust proxy', true);
When this option is set express will check the X-Forwarded-Proto header for which protocol was used to connect to the proxy server (in this case the load balancer). This header contains either http or https depending on the connection protocol.
For Azure though this is still not sufficient. The Azure load balancer does not set the X-Forwarded-Proto header either. Instead it uses x-arr-ssl. This is not a big problem though as iisnode (the runtime I'm using to run node on IIS in Azure) has an option called enableXFF that will update the X-Forwarded-Proto header based on the external protocol of the connection. Setting both these options enables express-session to set the secure cookie keeping the session stored and allowing passport-azure-ad to store and compare state information on authentication.
PS: Big thanks to Scott Smiths blog + comments for providing the answer:
http://scottksmith.com/blog/2014/08/22/using-secure-cookies-in-node-on-azure/
This is a known encode issue with module passport-azure-ad. See:
"State" gets encoded and causes "collectInfoFromReq: invalid state received" #309
"invalid state received in the request" causing infinite loop on Login #247
You could upgrade the module version to v3.0.7 or a newer one to fix it.

Connect mqtt client via web sockets with HTTPS from browser

I would like to run a mqtt client on a web browser using web sockets with HTTPS. With HTTP, I have no problem. Here is the code on the web browser when using HTTP.
<script>
var client = mqtt.connect( 'wss://127.0.0.1:3000', {username:'test_user', password:'test_password'} );
client.subscribe("mqtt/test");
client.on("message", function(topic, payload) {
alert([topic, payload].join(": "));
client.end();
});
client.publish("mqtt/test", "testing hello world!");
</script>
This is how I start the stand-alone mosca broker to use HTTPS on websockets.
mosca --very-verbose --key ./tls-key.pem --cert ./tls-cert.pem --credentials ./credentials.json --https-port 3000 --https-bundle --https-static ./ | pino
How should I change my mqtt client code on the browser to connect to the Mosca broker on websockets via HTTPS?
As discussed in the other questions you have asked, the web browser has it's own list of trusted CA certificates, your self signed certificate will not be in this list so the connection is going to fail.
You can import your own trusted certs into your browser, but how to do this differs with each browser and you have to do it for EVERY instance of the browser so only really useful for individual testing.
If you need to allow members of the public (or browsers you can't install your certificate on) to connect to your broker then you will have to get a certificate from a recognised CA. You will have to either pay for this or use a service like http://letsencrypt.org
You have problems due to the use of self-signed certificate
Instead - you can use:
service cloudflare as front (with https and wss in free plan). Read about cloud flare
Get Temporary sertificates from letsencrypt (has a free plans). Read about letsencrypt
Get Trusted paid certificate

Resources