TLS Mutual Authentication on Azure App Service with Node.js
Recently we faced the problem how to implement a client certificate validation or so called mutual authentication with a Node.js microservice on Azure App Service. Unfortunately, the corresponding Azure documentation doesn't provide a Node.js example for that and is kind of outdated.
This is the second post of a blog series regarding ‘Microservices with Node.js, TypeScript and Microsoft Azure’. In this series we will keep you up with different parts of this topic. (First Post / Third Post)
There are multiple authentication methods that can be used for App Services. Mutual authentication is only one of them. The main purpose is to enforce a client to provide a certificate over TLS/SSL to authenticate. The validation of this certificate takes place on the server side. Inside of an Azure Web App we get requests from a back end that authenticates itself by a client certificate by default. Therefore we had no other option but to implement it.
How it’s not done
Normally it is possible to implement Client Certificate Validation in Node.js using the https package. Therefore one could simply add the required Client Certificate by providing an options object like that:
Https server creation with Node.js (TypeScript)
But unfortunately, in case of an Azure Web App this is NOT possible, because the service is not available directly from the internet. Basically a request from outside of Azure is being proxied through some components – like a load balancer for example – first. Only then it is received by the Node.js server. The Azure App Service or to be more precise the underlying IIS also terminates the SSL connections. After that it routes the request to the Node.js server without any encryption (http not https).
As just shown, it is not an option to use the regular Node.js https approach in the service. Instead there is a way that is described on Azure docs, but as already mentioned, that is not giving details on a Node.js implementation.
As already described, Azure is terminating the SSL connection before the Node.js server gets the request. In order to forward the client certificate, it writes the certificate as base64 encoded string to the “X-ARR-ClientCert” header and adds it to the proxied request. So the Node.js / express server can validate the certificate and react according to the input. In the next sections it is shown how one can implement the validation in Node.js and how to configure Azure such that everything works.
The coding part
The key is to register a middleware that gets the “X-ARR-ClientCert” header and subsequently uses node-forge to convert the incoming base64 encoded PEM string into a certificate object. Which, in turn, can then be validated as shown in the example:
Please be aware: This is a SAMPLE verification routine. Depending on your application logic and security requirements, you should modify this method according to your needs. The code shown above, for example, does not contain a test if the certificate chains to a Trusted Root Authority, because in our use case the certificate is self signed. If you want to add this test as well, you could for example simply use the “verify” utility function that node-forge provides to you (documented here).
The configuration part
In Azure it is necessary to enable “HTTPS Only” in order to enforce SSL connections and enable “Client Certificates” to tell the IIS Server to add the “X-Arr-ClientCert” header. Otherwise the certificate will not be appended to the proxied request. This is done by changing it inside of the “SSL settings” of the App Service like shown in the picture below.
Now you should be able to get the client certificate from the request object and to validate the certificate according to your needs.