Turn on client certificate authentication (preview)
You can restrict access to your instance of Azure Web PubSub by turning on different types of authentication for the resource. One authentication method is to request a client certificate and validate the certificate in event handlers. This mechanism is called client certificate authentication or Transport Layer Security (TLS) mutual authentication. This article shows you how to set up your Web PubSub instance to use client certificate authentication.
Note
Enabling client certificate authentication in a browser scenario generally is not recommended. Different browsers have different behaviors when they process a client certificate request, and you have little control in a JavaScript application. If you want to enable client certificate authentication, we recommend that you use it in scenarios in which you have strong control over TLS settings. An example is in a native application.
Prerequisites
- An Azure account with an active subscription. If you don't have an Azure account, you can create an account for free.
- An Azure Web PubSub instance at a minimum Standard tier.
- A function created in Azure Functions to handle connect events.
- A client certificate. You need to know its SHA-1 thumbprint.
Deploy Web PubSub
In this example, you use a function called func-client-cert
as an event handler to process connect
events. Clients connect to a hub called echo
. The next sections have Bicep and Azure Resource Manager templates that you can use to deploy an Azure Web PubSub service with client certificate authentication enabled and event handlers configured.
The templates enable client certificate authentication via the property tls.clientCertEnabled
.
The templates configure an event handler for the connect
event to validate the client thumbprint. Also note that anonymousConnectPolicy
is set to allow
so that clients no longer need to send access tokens.
Bicep
param name string
param hubName string = 'echo'
param eventHandlerUrl string = 'https://func-client-cert.azurewebsites.net/api/echo'
param location string = resourceGroup().location
resource awps 'Microsoft.SignalRService/WebPubSub@2023-03-01-preview' = {
name: name
location: location
sku: {
name: 'Standard_S1'
tier: 'Standard'
size: 'S1'
capacity: 1
}
properties: {
tls: {
clientCertEnabled: true
}
}
}
resource hub 'Microsoft.SignalRService/WebPubSub/hubs@2023-03-01-preview' = {
parent: awps
name: '${hubName}'
properties: {
eventHandlers: [
{
urlTemplate: eventHandlerUrl
userEventPattern: '*'
systemEvents: [
'connect'
]
}
]
anonymousConnectPolicy: 'allow'
}
}
Azure Resource Manager
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"name": {
"type": "String"
},
"hubName": {
"defaultValue": "echo",
"type": "String"
},
"eventHandlerUrl": {
"defaultValue": "https://func-client-cert.azurewebsites.net/api/echo",
"type": "String"
},
"location": {
"defaultValue": "[resourceGroup().location]",
"type": "String"
}
},
"resources": [
{
"type": "Microsoft.SignalRService/WebPubSub",
"apiVersion": "2023-03-01-preview",
"name": "[parameters('name')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_S1",
"tier": "Standard",
"size": "S1",
"capacity": 1
},
"properties": {
"tls": {
"clientCertEnabled": true
}
}
},
{
"type": "Microsoft.SignalRService/WebPubSub/hubs",
"apiVersion": "2023-03-01-preview",
"name": "[concat(parameters('name'), '/', parameters('hubName'))]",
"dependsOn": [
"[resourceId('Microsoft.SignalRService/WebPubSub', parameters('name'))]"
],
"properties": {
"eventHandlers": [
{
"urlTemplate": "[parameters('eventHandlerUrl')]",
"userEventPattern": "*",
"systemEvents": [
"connect"
]
}
],
"anonymousConnectPolicy": "allow"
}
}
]
}
Validate a client certificate in an event handler
You can validate an incoming client certificate via its SHA-1 thumbprint in the connect
event. The value is available in clientCertificates
. For more information, see CloudEvents HTTP extension for event handler.
The following code sample has function code that you can use to implement validation logic.
JavaScript
module.exports = async function (context, req) {
// For client connect event
if (req.headers && req.headers['ce-type'] == 'azure.webpubsub.sys.connect') {
// CLIENT_CERT_THUMBPRINT should be configured as an environment variable of valid client certificate SHA-1 thumbprint
var validCertThumbprint = process.env['CLIENT_CERT_THUMBPRINT'];
var certThumbprint = null;
if (req.body.clientCertificates) {
certThumbprint = req.body.clientCertificates[0].thumbprint;
// Certificate content in PEM
var certContent = req.body.clientCertificates[0].content;
var cert = new crypto.X509Certificate(certContent);
console.log('Client cert:', cert);
}
if (certThumbprint != validCertThumbprint) {
context.log('Expect client cert:', validCertThumbprint, 'but got:', certThumbprint);
context.res = {
status: 403
};
return;
}
}
context.res = {
// status: 200, /* Defaults to 200 */
headers: {
'WebHook-Allowed-Origin': '*'
},
};
}
Rotate the certificate
If you want to rotate the certificate, you can update your event handler code to accept multiple thumbprints.
Handle a missing client certificate
Azure Web PubSub doesn't abort a TLS handshake when a client doesn't provide a client certificate. It's up to the event handler to decide whether to accept or reject a connection without a client certificate.