Azure Event Grid series: Authenticate Webhook subscriptions
Tags: azure, eventgrid, security, tip
Published: 2020-12-10 13:07:57

This is a series of blogs to talk and discuss about good practices and tips for Event Grid. Some of the topics that will be discussed can be applied not only to Event Grid but also to any other event/message based service.

## TL;DR

Webhook subscriptions are a common way to receive events from Azure Event Grid. It is essential to secure the endpoint and make Azure Event Grid capable of authenticating request to the subscription. Using a secret as query parameter is basic solution to provide authentication and security the Webhook subscription.

## Webhook subscription

According to [Wikipedia](https://en.wikipedia.org/wiki/Webhook#Function), Webhooks are HTTP callbacks that are usually triggered by some event. On Azure Event Grid, **Webhooks** are a way to receive events. Those events are delivered as POST requests with the event on the payload.

As in many other event based services, it is necessary to validate and prove that the user defined **Webhook** is able to receive and process events. On Event Grid, that [validation process](https://docs.microsoft.com/en-us/azure/event-grid/webhook-event-delivery#endpoint-validation-with-event-grid-events) is usually done in a synchronous way, it sends a special request to the endpoint and expects a specific response from the Webhook. When the validation finishes, the subscription is created and events are started to being delivered to it. This validation process might vary depending on the event schema used on Azure Event Grid. For example, [Cloud Event Schema](https://docs.microsoft.com/en-us/azure/event-grid/cloudevents-schema#endpoint-validation-with-cloudevents-v10) receives the validation request on a OPTIONS request instead of a POST.

## Securing a Webhook

As we have discussed before, Webhooks are endpoints where you can receive events, in this case from Azure Event Grid. If this endpoint is public it is crucial to secure it in order to avoid maliciousrequests. On a secured endpoint, unauthenticated requests are discarded and not processed on the Webhook, therefore the question here is: how is it possible to authenticate the delivered requests from Event Grid?

A very common way to secure an endpoint on Event Grid is [using secrets as query parameters](https://docs.microsoft.com/en-us/azure/event-grid/security-authentication#using-client-secret-as-a-query-parameter). It is quite simple, it is just necessary to ask Event Grid to deliver an extra parameter on every event as a query parameter, that extra parameter is simply a password that both services (Event Grid Topic and Event Grid Subscription) know.

This work can be split in three steps:

1. **Create a common secret or password:** it is possible to use a [script](https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-powershell#adding-a-secret-to-key-vault) or [ARM template](https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-template?tabs=CLI) to create and store a secret on a KeyVault. That secret can be retrieved later when creating the Webhook subscription or when proving the authentication of the delivered event.

2. **Create Webhook subscription with secret:** when creating the subscription via Portal or ARM template it is possible to specify query parameters, so every event will be delivered along with the query parameter. Because it is common to have secrets on those query parameters, they are handled in a special way on Event Grid. By default, those parameters are hidden when retrieving subscription information and from service operators, they are encrypted, not logged, etc. For example, Webhook endpoint on the Azure Portal (picture 1) hides the query parameters. On the other hand, CLI command (picture 2) shows the query parameters when using `--include-full-endpoint-url` parameter.

3. **Validate secret on subscription endpoint:** last but not least, it is necessary to validate the received secret. The expected secret can be easily read from the KeyVault from step 1 and passed as configuration parameter to the subscription endpoint. If working with .NET core, the validation can be done in a custom `ActionFilterAttribute`:

*Picture 1: Event Grid subscription - Portal*

![Subscription information - Portal](https://dev-to-uploads.s3.amazonaws.com/i/eutcdom5ntqgekctroj7.PNG)

*Picture 2: Event Grid subscription - CLI*

![Subscription information - CLI](https://dev-to-uploads.s3.amazonaws.com/i/sj3hp0tv2joth0wpe26g.png)

```csharp
public class WebhookAuthenticationAttribute : ActionFilterAttribute
{
    private static string Secret = "my-secret";

    public override async Task OnActionExecutionAsync(ActionExecutingContext context ActionExecutionDelegate next)
    {
        var queryKey = context.HttpContext.Request.Query["key"];

        if (queryKey != Secret)
        {
            context.Result = new UnauthorizedObjectResult("Authentication failed. Please use a valid key.");
        }

        await base.OnActionExecutionAsync(context, next);
    }
}

[WebhookAuthentication]
public class MyWebhookController : ControllerBase
{
    [HttpPost]
    public IActionResult NewEvent()
    ...
}
```

### Alternatives

Secrets as query parameters is not the only way to secure a Webhook subscription. Alternatively, it is possible to secure the endpoint with [Azure AD](https://docs.microsoft.com/en-us/azure/event-grid/security-authentication#using-azure-active-directory-azure-ad).

Furthermore, other techniques like [Claim check pattern](https://dev.to/davidgsola/tips-and-tricks-for-event-grid-part-i-claim-check-pattern-a71) can add an extra layer of security. With this pattern events payload are stored in an external storage and the delivered event includes a reference to its storage. In this case, it will be necessary to authenticate the request to the external storage in order to download the information and process the full event.