Deployment slots on azure and authentication

When using the dotnet identity framework the issue of users getting logged off when deploying and switching deployment slots arise. Here comes a solution to that problem.

Before using deployment slots, you're essentially running a single web app and azure provides a default configuration which just makes it work. It's using the DPAPI which is never meant to be used outside of the machine (MSDN Data protection, encryption at rest). However, when adding deployment slots, you're essentially adding another web app which uses a different data protection key.

To solve this, you have to configure the DataProtection provider that is used behind the scenes. You can configure this in multiple ways, using for instance certificates or azure data vault keys. The simplest way is probably to use certificates. You could use your https certificate for this, but remember to add the appsettings "WEBSITE_LOAD_CERTIFICATES" with the thumbnail. See the instructions if you are unsure of how: https://azure.microsoft.com/en-us/blog/using-certificates-in-azure-websites-applications/

Aside from that, you also have to store the data (generated keys) somehow. There are some possibillities there as well, but since the idea is to remove the statefulness on the server, it's probably a good idea to use blob storage or similar instead of local storage, even though that could work if it's mapped between the servers. The data that is stored is just XML and it's fairly trivial to roll your own storage provider (IXmlRepository interface, GetAllElements() and StoreElements(XElement element, string friendlyName)).

To setup dataprotection using a certificate and blob storage, se the code below. The SSL certificate (https) will suffice, thought the behaviour if the certificate is "rekeyed" is yet unknown for me.

services.AddDataProtection()
.SetApplicationName("myApplicationName")
.ProtectKeysWithCertificate(thumbprint)
.PersistKeysToAzureBlobStorage(cloudStorageAccount, "containerName/keys.xml");

And voialá, deployment without users getting logged off!

It should also be mentioned that if the code above is used, the container [containerName] will have to be created before usage.

The XML created from this looks like this when created with Microsoft.NetCore.DataProtection, version v2.1.1:

<?xml version="1.0" encoding="utf-8"?>
<repository>
<key id="ca70b23c-bfd3-4d5c-a43e-ff18aa157bcf" version="1">
<creationDate>2019-01-15T18:43:31.2601683Z</creationDate>
<activationDate>2019-01-15T18:43:30.7738864Z</activationDate>
<expirationDate>2019-04-15T18:43:30.7738864Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<encryptedSecret decryptorType="Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"
xmlns="http://schemas.asp.net/2015/03/dataProtection">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>xxx</X509Certificate>
</X509Data>
</KeyInfo>
<CipherData>
<CipherValue>yyy</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>zz</CipherValue>
</CipherData>
</EncryptedData>
</encryptedSecret>
</descriptor>
</descriptor>
</key>
</repository>