I have an ASP.NET Core Web App, and I want to use Azure AD authentication. I've got it all set up and working locally, but when I deploy it to my Service Fabric instance, I'm getting errors on internal API calls, because it attempts to login again on the API call.
After some digging, I discovered that by default, the Machine Key is used to encrypt the cookie. My SF instance has multiple machines running, which seems to be the culprit (different machines cannot read the same cookie, therefore, thinks I am not authenticated).
Is there a way to get the AAD cookie authentication to use the same key between machines?
This is what I currently have in my ConfigureServices
call:
services.AddDataProtection()
.SetApplicationName("my-app")
.ProtectKeysWithCertificate("my-thumbprint")
.PersistKeysToFileSystem(new DirectoryInfo("dp-keys"));
But this still doesn't seem to work - a different key is still generated on each machine. At the moment, I don't have a way to share a file between all the machines - would that be the only solution, or would there be something else as well?
Since we are on service fabric, you properly already have a cert deployed that your cluster can use. Otherwise you would have to add a certificate.
Then you can do the following:
var cert = X509.LocalMachine.My.Thumbprint.Find("C03BB5A6410741CDD2927B4FF88C3E67215A393B", validOnly: false).FirstOrDefault();
services.AddApplicationStorageDataProtection(_container, cert);
and
public static IServiceCollection AddApplicationStorageDataProtection(this IServiceCollection services, IUnityContainer container, X509Certificate2 cert )
{
if (container != null)
{
try
{
var storage = container.Resolve<IApplicationStorageService>();
var token = storage.GetApplicationStorageSharedAccessSignature().GetAwaiter().GetResult();
var name = storage.GetApplicationStorageAccountNameAsync().GetAwaiter().GetResult();
var a = new CloudStorageAccount(new StorageCredentials(token), name, null, true);
var c = a.CreateCloudBlobClient().GetContainerReference("dataprotection");
c.CreateIfNotExists();
services.AddDataProtection()
.ProtectKeysWithCertificate(cert)
.PersistKeysToAzureBlobStorage(c.GetBlockBlobReference("dummy.csrf"));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
return services;
}
You would of cause have to change the code to your needs. The code provided here is using a deployed storage service in the cluster, such I dont need to manage credentials for storage in all my applications, but simply can ask the storage service for the credentials, and also the unity container is just there because this is the underlying DI framework that i am using.
So the part that you need is:
services.AddDataProtection()
.ProtectKeysWithCertificate(cert)
.PersistKeysToAzureBlobStorage(c.GetBlockBlobReference("dummy.csrf"));