When using Azure API Manager, there are 3 main ways to authenticate a request before passing it to a backend.
- Using Azure APIM inbuilt subscription keys.
- Using a JWT token from another service and validating it using OpenID
- Using a pre-shared certificate and validating the incoming request with the stored certificate
The latter may seem like a rare occurrence, but if you are doing machine to machine requests – for example two backend services need to talk to each other, then signing the requests with certificates is surprisingly handy.
Turning On Negotiate Client Certificate
For whatever reason, the documentation from Microsoft is fairly sparse about a very important setting you must turn on. Even if you configure APIM to validate certificates, none of it will work if you don’t first turn on “Negotiate Client Certificate”.
How you do that depends on your APIM SKU. For *non-consumption* APIM SKUs, you must go to your APIM, look for “Custom Domains” on the left hand menu, select your domain, and then tick the box to turn client certificates on :
For consumption APIM SKUs, the setting is still under Custom Domains, but you will instead have a slider to turn it on globally :
Uploading Client Certificate
Next, we need to upload the certificate to APIM. There are two ways this can be done :
- Manually upload the certificate directly into API Manager
- Upload a certificate into Azure Keyvault and then reference it via API Manager
I highly recommend the latter for a couple of reasons.
- Should you update the certificate, you only need to upload the new version into Key Vault and everything referencing it will automatically update
- Key Vault can generate self signed certificates for you automatically, which is secure when you yourself are the client.
- The client code does not need to have a local version of the certificate and can reference Key Vault also (So there is no risk of checking in the certificate to source control or uploading it to a developer wiki etc)
To reference a certificate from APIM. Simply select Certificates from the left hand menu, make sure you select “Client Certificates” on the next blade, and then you can add a certificate directly from Key Vault :
Validating Client Certificate Policy
With your certificate uploaded, you can now validate incoming certificates with an Inbound Processing policy.
The easiest way to do so is with a policy that checks and validates the incoming certificate, and if invalid, returns a 403 response. Something like so :
<when condition="@(context.Request.Certificate == null || !context.Request.Certificate.Verify() || !context.Deployment.Certificates.Any(c => c.Value.Thumbprint == context.Request.Certificate.Thumbprint))"> <return-response> <set-status code="403" reason="Invalid client certificate" /> </return-response> </when>
All this does is check :
- Is there a certificate
- Can we verify it
- Does the certificate match a certificate that we have uploaded to APIM
If any of that is false, then we return a 403 response!
Some of this can be a bit finicky, so here’s some things that helped me!
If the following is always empty after you’ve uploaded a certificate :
Then it’s almost certain that you have not turned on the “Negotiate Client Certificate” at the start of this article. Not turning on this setting does not give you a warning or error, it just does not load certificates into policies.
Inbuilt Client Certificate Policy
Supposedly, there is an inbuilt policy for validating client certificates using XML like so :
<validate-client-certificate> <identities> <identity /> </identities> </validate-client-certificate>
I never managed to get this to work for me. It’s possible that the magical incantation of settings wasn’t set up quite right, but even when I went through Microsoft’s documentation they didn’t use this. From the documentation it was also unclear if this XML could/would validate against uploaded certificates, or if it would just validate against a hardcoded thumbprint. So for now, don’t use it!
Authenticate Certificate Policy
There is also a XML policy for “Authenticate-Certificate” like so :
<authentication-certificate thumbprint="thumbprint" certificate-id="resource name"/>
As far as I could work out, this is for authenticating with backend services with a certificate. It is not for clients calling your API manager. So again, skip this one!