I'm encountering an InvalidOperationException in my ASP.NET Core application when integrating with IdentityServer4. In local everything works fine but when I replace my old projects (Client + Auth) I get this error message and many more but this one was the first one I saw in Event Viewer. The error message is:
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).
at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.<>c__DisplayClass0_0.<<HandleAsync>g__Handle|0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|8_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
Environment
Client Application: ASP.NET Core 7
IdentityServer: ASP.NET Core 8
Hosting: IIS
Client Application Configuration
Here is my Program.cs file for the client application:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// Configure authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/account/login";
options.LogoutPath = "/account/logout";
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = builder.Configuration["Auth"];
options.ClientId = "MyAPP";
options.ClientSecret = "secret3";
options.ResponseType = "code";
options.RequireHttpsMetadata = true;
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.CallbackPath = new Microsoft.AspNetCore.Http.PathString("/signin-oidc");
options.Scope.Add("openid");
options.Scope.Add("profile");
options.SaveTokens = true;
options.UseTokenLifetime = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
NameClaimType = "name",
RoleClaimType = "role",
};
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(90);
return Task.CompletedTask;
},
OnRemoteFailure = context =>
{
context.Response.Redirect("/");
context.HandleResponse(); // Prevents default exception
return Task.CompletedTask;
},
OnAuthorizationCodeReceived = context =>
{
return Task.CompletedTask;
}
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
IdentityServer Configuration
Here is my Startup.cs file for the IdentityServer:
using IdentityServer.Quickstart.Account;
using IdentityServer4.Services;
using IdentityServer4.Validation;
using IdentityServerHost.Quickstart.UI;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace IdentityServer
{
public class Startup
{
public IWebHostEnvironment Environment { get; }
public Startup(IWebHostEnvironment environment)
{
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddCors(options =>
{
options.AddPolicy(name: "AuthCorsPolicy",
builder =>
{
builder.WithOrigins("http://localhost:5001",
"http://localhost:44314",
"http://localhost:44396")
.AllowAnyHeader()
.AllowCredentials()
.AllowAnyMethod();
});
});
services.AddSingleton<ICorsPolicyService>((container) =>
{
var logger = container.GetRequiredService<ILogger<DefaultCorsPolicyService>>();
return new DefaultCorsPolicyService(logger)
{
AllowedOrigins = { "http://localhost:5001", "http://localhost:44314", "http://localhost:44396" },
};
});
services.AddControllersWithViews();
services.AddScoped(_ => new HorizonUsersFetcher());
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie();
services.AddAuthorization();
var builder = services.AddIdentityServer().AddCorsPolicyService<DefaultCorsPolicyService>();
// Configurations for different environments
builder.AddInMemoryIdentityResources(Config.IdentityResources);
builder.AddInMemoryApiScopes(Config.ApiScopes);
builder.AddInMemoryApiResources(Config.ApiResources);
builder.AddInMemoryClients(Config.Clients);
builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
builder.Services.AddTransient<IProfileService, ProfileService>();
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
string certName = XDocument.Parse(File.ReadAllText("Web.config")).Descendants("add").Where(x => x.Attribute("key").Value == "CertificateName").Select(x => x.Attribute("value")).FirstOrDefault().Value;
builder.AddSigningCredential("CN=" + certName);
}
}
public void Configure(ILoggerFactory _loggerFactory, IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSession();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("AuthCorsPolicy");
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}
Troubleshooting Steps Taken
Verified Configuration:
Confirmed that the DefaultScheme and DefaultChallengeScheme are set correctly in both client and server.
Ensured AuthenticateurURL in appsettings.json points to the correct IdentityServer URL.
Logging:
Implemented Serilog in both client and server applications for detailed logging.
Reviewed logs but did not find additional information on the cause of the issue.
IIS Logs:
Checked IIS logs for any additional details but found nothing conclusive.
Questions
Middleware Order:
Could there be an issue with the order of middleware in Startup.cs?
Is there a specific order that UseAuthentication and UseAuthorization should follow?
Configuration Issues:
Are there any common misconfigurations that could lead to this issue?
How can I further debug this problem to identify the root cause?
Logging and Diagnostics:
What additional logging or diagnostics can I enable to get more insights into this issue?
Any insights or suggestions on how to resolve this issue would be greatly appreciated. Thank you!