I am trying to setup OpenIddict using ASP.NET Core DataProtection however I keep getting an exception when attempting to start the application:
InvalidOperationException: At least one encryption key must be registered in the OpenIddict server options. Consider registering a certificate using 'services.AddOpenIddict().AddServer().AddEncryptionCertificate()' or 'services.AddOpenIddict().AddServer().AddDevelopmentEncryptionCertificate()' or call 'services.AddOpenIddict().AddServer().AddEphemeralEncryptionKey()' to use an ephemeral key.
Now I understand what the error wants me to do, I need to add a signing key/encryption certificate, but why?
Isn't the whole point of using the DataProtection that this part is automated and doesn't require you to add an explicit signing key/encryption certificate? According to the documentation the DataProtection should be used to encrypt all the keys except for JWT id tokens.
This is the current code:
if (authenticationOptions.DataProtection.TokenProtection.Enabled)
services
.AddDataProtection(options =>
{
options.ApplicationDiscriminator = applicationOptions.ShortName;
})
.SetApplicationName(applicationOptions.Name)
.SetDefaultKeyLifetime(TimeSpan.FromDays(authenticationOptions.DataProtection.TokenProtection.LifeTime))
.PersistKeysToAzureBlobStorage(CreateDataProtectionBlobClient(azureOptions.BlobStorageUrl, authenticationOptions.DataProtection, azureCredential))
.ProtectKeysWithAzureKeyVault(completeKeyVaultUri, azureCredential);
}
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>()
.ReplaceDefaultEntities<long>(); ;
})
.AddServer(options =>
{
options.SetAuthorizationEndpointUris("connect/auth");
options.SetTokenEndpointUris("/connect/token");
options.AllowAuthorizationCodeFlow();
options.AllowClientCredentialsFlow();
options.AllowRefreshTokenFlow();
options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough()
.EnableTokenEndpointPassthrough();
options.UseDataProtection();
//options.AddEphemeralSigningKey();
//options.AddEphemeralEncryptionKey();
})
.AddValidation(options =>
{
options.UseLocalServer();
options.UseAspNetCore();
options.UseDataProtection();
});
The data protection API is involved inside the Cookie handler, and its job is to encrypt the session cookie. The session cookie contains the ClaimsPrincipal User object and optionally your tokens.
Just like this picture shows:
By default the Data Protection API creates its own key-ring and that's great! But in production you want to make sure the keyring stays the same across deployments. If not, if you redeploy, then you might not be able to accept previously generated cookies.
I did blog about this here: https://www.edument.se/post/storing-the-asp-net-core-data-protection-key-ring-in-azure-key-vault?lang=en
However, the Data Protection API is not involved in managing the token signing keys. Data Protection is only about protecting the session cookie. So you need to provide a separate private key (persisted externally), so the key is the same across deployments of your service.
Data Protection API is only about protecting things, not what to protect. So that's why you need a separate key/certificate.
I did a blog post about how you can bypass the protection and peek inside the cookies at: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With