Harnessing ASP.NET Core MVC to Create Custom Value Providers for Encrypted Route Parameters
Often it is necessary to share anonymously accessible URLs in such a way that only the intended users can access a particular page and its details. Strictly speaking, it could be argued that if a page is only intended for a specific user, it should be behind the login flow. However, we also need to recognize that way is often not the most convenient from the end-user perspective, especially when users need to share specific details with guest users. There are many business scenarios in which this is the case: just one example we have all encountered is when we need to track an order shipment. The best way to create such anonymously accessible URLs is to encrypt a key identifier in the URLs. For our order tracking example, that would be the order ID. This is important in preventing malicious users from gaining direct access to IDs by piecing together various identifiers.
The traditional approach
The classic approach to reading encrypted values from HTTP requests or URLs is to read query strings or route values and decrypt them manually as explained below.
In the case of ASP.NET Core MVC/Web API, the route value is directly bound with the action method parameter and from there we must manually decrypt it. The only time this gets complicated is when this encrypted string contains multiple values and we must read each of the decrypted values manually and classify them according to data type.
Take a look at code snippets below. This manual approach is straightforward enough for a relatively small application with only a handful of MVC actions accepting an encrypted value. However, consider an application where there are numerous action methods reading encrypted values from the route parameter.
With the help of ASP.NET Core extensibility features, we can delegate this responsibility of decrypting route parameters and type casting to the custom value provider. After implementing ASP.NET Core’s custom value provider, CryptoValueProvider, the above code snippets can be rewritten as below.
As we can see that above code snippet is much more readable and it directly sets strongly typed decrypted value in action methods parameters.
Leveraging ASP.NET Core extensibility
Here we will dive into a step-by-step walkthrough of how to create custom value providers in ASP.NET Core. In a nutshell, value providers feeds data to model binders.
The first step is to implement CryptoParamsProtector.cs with the help of ASP.NET Core Data Protection APIs. This CryptoParamsProtector class offers two methods. EncryptParamDictionary – which will accept parameter dictionary and will encrypt it to single string. Another method that the DecryptToParamDictionary will accept the encrypt string and will convert it back to the parameter dictionary after decrypting with data protection APIs.
Next we need to implement CryptoBindingSource.cs. In the CryptoBindingSource class, we define the BindingSource object which represents the binding source for CryptoValueProvider i.e. the encrypted value from the route parameter. This binding source is useful when we want to bind the action method parameter value from the encrypted string as demonstrated in the earlier method.
Lastly there is CryptoValueProvider.cs. This is the CryptoValueProvider class where we actually implement the custom value provider in ASP.NET Core. Generally, we implement the IValueProvider interface to create the custom value provider. However here we are inheriting it from BindingSourceValueProvider to also support parameter level binding.
Now that we have created the CryptoValueProvider, we have to instantiate it and plug it in when model binding takes place for our targeted action method. We will create CryptoValueProviderFactory to accomplish this.
Next, we will create CryptoValueProviderAttribute which will be used to decorate the action method to denote that all the parameters of this method will be bound with the help of CryptoValueProvider.
Using the same steps as before we will create FromCryptoAttribute, which can be used to define the optional binding source at parameter level. The rest of the action parameters will be bound with the help of the default Value Provider.
Lastly, we will register CryptoParamsProtector and CryptoValueProviderFactory in Startup.ConfigureServices to wire-up altogether.
Example usage
First, we will create the encrypted string with the secret value as in below example controller.
Use encrypted string to construct URL and share it with user through email or direct link.
If you’d like to know more about ASP.NET Core development, complete the form below.