I'm trying to add identity server authentication to a .NET Core 3 API project.
I've added this code
public void ConfigureServices(IServiceCollection services)
{
…
var identityBuilder = services.AddIdentityServer();
identityBuilder.AddApiAuthorization<ApplicationUser, DbContext>();
services
.AddAuthentication()
.AddIdentityServerJwt();
var fileName = Path.Combine("Certificates", "certificatefile.pfx");
var cert = new X509Certificate2(fileName, "veryDifficultPassword");
identityBuilder.AddSigningCredential(cert);
…
}
And:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
…
app.UseAuthentication();
app.UseIdentityServer(); // <--- this line throws error.
app.UseAuthorization();
…
}
There is a file which is read and loaded properly in the /Certificates
folder—I can inspect the cert
variable and it looks correct.
Everything I have tried so far ends with the line app.UseIdentityServer();
blowing up with:
System.InvalidOperationException: 'Key type not specified.'
Any suggestions?
Update: Including stacktrace
System.InvalidOperationException
HResult=0x80131509
Message=Key type not specified.
Source=Microsoft.AspNetCore.ApiAuthorization.IdentityServer
StackTrace:
at Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ConfigureSigningCredentials.LoadKey()
at Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ConfigureSigningCredentials.Configure(ApiAuthorizationOptions options)
at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
at Microsoft.Extensions.Options.OptionsManager`1.<>c__DisplayClass5_0.<Get>b__0()
at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy`1.CreateValue()
at System.Lazy`1.get_Value()
at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd(String name, Func`1 createOptions)
at Microsoft.Extensions.Options.OptionsManager`1.Get(String name)
at Microsoft.Extensions.Options.OptionsManager`1.get_Value()
at Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.<>c.<AddClients>b__7_1(IServiceProvider sp)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.AspNetCore.Builder.IdentityServerApplicationBuilderExtensions.TestService(IServiceProvider serviceProvider, Type service, ILogger logger, String message, Boolean doThrow)
at Microsoft.AspNetCore.Builder.IdentityServerApplicationBuilderExtensions.Validate(IApplicationBuilder app)
at Microsoft.AspNetCore.Builder.IdentityServerApplicationBuilderExtensions.UseIdentityServer(IApplicationBuilder app, IdentityServerMiddlewareOptions options)
at Boskapstorget.API.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in C:\Private\Kod\Boskapstorget\Backend\src\Boskapstorget.API\Startup.cs:line 86
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass13_0.<UseStartup>b__2(IApplicationBuilder app)
at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.<StartAsync>d__31.MoveNext()
The windows Authentication provider lets you authenticates users based on their windows accounts. This provider uses IIS to perform the authentication and then passes the authenticated identity to your code. This is the default provided for ASP.net.
I don't think that the code you have added to install a signing certificate has caused the problem. The code in the stacktrace is being executed because identityBuilder.AddApiAuthorization<ApplicationUser, DbContext>();
calls AddSigningCredentials()
which eventually configures code to look in appsettings.json for key definition Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ConfigureSigningCredentials
:
public SigningCredentials LoadKey()
{
var key = new KeyDefinition();
_configuration.Bind(key);
switch (key.Type)
{
case KeySources.Development:
var developmentKeyPath = Path.Combine(Directory.GetCurrentDirectory(), key.FilePath ?? DefaultTempKeyRelativePath);
var createIfMissing = key.Persisted ?? true;
_logger.LogInformation($"Loading development key at '{developmentKeyPath}'.");
var developmentKey = new RsaSecurityKey(SigningKeysLoader.LoadDevelopment(developmentKeyPath, createIfMissing))
{
KeyId = "Development"
};
return new SigningCredentials(developmentKey, "RS256");
case KeySources.File:
var pfxPath = Path.Combine(Directory.GetCurrentDirectory(), key.FilePath);
var pfxPassword = key.Password;
var storageFlags = GetStorageFlags(key);
_logger.LogInformation($"Loading certificate file at '{pfxPath}' with storage flags '{key.StorageFlags}'.");
return new SigningCredentials(new X509SecurityKey(SigningKeysLoader.LoadFromFile(pfxPath, key.Password, storageFlags)), "RS256");
case KeySources.Store:
if (!Enum.TryParse<StoreLocation>(key.StoreLocation, out var storeLocation))
{
throw new InvalidOperationException($"Invalid certificate store location '{key.StoreLocation}'.");
}
_logger.LogInformation($"Loading certificate with subject '{key.Name}' in '{key.StoreLocation}\\{key.StoreName}'.");
return new SigningCredentials(new X509SecurityKey(SigningKeysLoader.LoadFromStoreCert(key.Name, key.StoreName, storeLocation, GetCurrentTime())), "RS256");
case null:
throw new InvalidOperationException($"Key type not specified.");
default:
throw new InvalidOperationException($"Invalid key type '{key.Type ?? "(null)"}'.");
}
}
You are hitting the null
case because your appsettings.json or appsettings.Development.json file does not configure the Key
.
I can reproduce your problem in 2 ways:
Commenting out the key configuration in appsetting.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"IdentityServer": {
//"Key": {
// "Type": "Development"
//}
}
}
This assumes you are running\debugging in Development environment
The second way to reproduce is to configure to run in Production which doesn't have a Key
definition by default in appsettings.json
I think the solution to your problem would be to define the certificate file in appsettings.json or appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"IdentityServer": {
"Key": {
"Type": "File",
"FilePath": "Certificates\\certificatefile.pfx",
"Password": "veryDifficultPassword"
}
}
}
and remove this code
var fileName = Path.Combine("Certificates", "certificatefile.pfx");
var cert = new X509Certificate2(fileName, "veryDifficultPassword");
identityBuilder.AddSigningCredential(cert);
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