If you are deploying a .net core application to a hosting provider and you are getting this issue:
System.InvalidOperationException: The antiforgery token could not be decrypted. ---> System.Security.Cryptography.CryptographicException: The key {9725081b-7caf-4642-ae55-93cf9c871c36} was not found in the key ring.
There is a high probability that you are getting this issue because your data protection keys
are not matching.
It took me ~2 days to figure out what was going on, so after I found the solution I decided to write a blog post so it could be helpful to other people facing the same issue.
What is the issue?
I ran into this issue after publishing my application to a hosting provider (A2Hosting), but I have seen that this problem happens when you try to run your web app in Azure App Service or other hosting providers.
And this is what I found in my log files. The error is simple but misleading:
System.InvalidOperationException: The antiforgery token could not be decrypted.
If this error would happen locally then there is a high chance that you have 2 AntiforgeryTokes in the same view, which causes this issue. But, in my local machine, everything was running smoothly. The problem was only on the server.
While digging deeper I also found another error that is even more confusing:
CryptographicException was thrown saying The key {just-a-guid-value} was not found in the key ring
.
However everything worked fine when running the app on my local machine, but when deployed it was just randomly logging out the users.
No certain pattern to replicate the issue, no defined timeout. Just random click and random log out. That is all I got.
Since everything was running fine in my local machine, I figured out this had something to do with how things work in my hosting provider.
I also tried to redeploy the app and other stuff, but still, no solution was on the sight.
After doing lots of deployments, I was starting to see a pattern. The error occurred every other time I deployed new code to the site and it seemed like it had something to do with how swapping between different slots (development, staging, production) is working.
The Data Protection capabilities in ASP.NET Core is used to protect data, i.e. when you want to round trip some data via a not trusted client. You can read more about Data Protection in the ASP.NET Core documentation.
When not using deployment slots, everything works fine because the data protection keys stored on disk is synchronized across all the machines hosting your web app, but when using a deployment slot, you will end up with two separate keys. I assumed this would “just work” when running ASP.NET Core in any hosting provider, but I was wrong.
When you swap between deployment slots, for example swapping Development to Production, any system using data protection will not be able to decrypt stored data using the keyring inside the previous slot. This will lead to users being logged out of an ASP.NET application that uses the standard ASP.NET cookie middleware, as it uses data protection to protect its cookies. If you desire slot-independent key rings, use an external keyring provider, such as Azure Blob Storage, Azure Key Vault, a SQL store, Redis cache or you can even use your file system to store the key.
In addition to problems with anti-forgery tokens, this problem also applies to authentication cookies, so users who are logged in when you deploy new versions and swap between staging and deployment, will also experience this issue.
Solution
So how can we fix this problem?
After digging through various articles, Github issues it turns out that you can configure Data Protection to store keys to Azure Blog Storage, Redis instance, or even File System. So, I decided to go with the easiest option, I decided to use the File System.
And here are the 3 simple steps to fix the issue:
- First of all, you need to install this package:
Install-Package Serilog.Extensions.Logging.File
- And then add the following lines of code in the ConfigureService method:
public void ConfigureServices(IServiceCollection services)
{
//your code here
services.AddDataProtection()
.SetApplicationName("your-app-name")
.PersistKeysToFileSystem(new DirectoryInfo("your-path-here"));
//your code here
} - Deploy you app
This line of code with creating a new key in the defined path and everything will run as expected.