I have a blazor webassembly hosted solution that is using identity for authentication. Recently I realized that pre-render is not working correctly as the very first load of my website shows only the "loading" in the network preview and not the pages html.
I went through and verified that my application is set up for pre-render, however, now I keep getting the following error on the sites launch
"InvalidOperationException: Cannot provide a value for property 'AuthenticationStateProvider' on type 'Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState'. There is no registered service of type 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider'."
I have searched on here and the only answer is make sure the clients Program.cs has the following added:
builder.Services.AddAuthorizationCore();
builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<RolesClaimsPrincipalFactory>();
Well mine does and I am still running into these issues. Can someone help point me in the right direction
Server - Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//Register the Datacontext and Connection String
services.AddDbContext<DataContext>(options =>
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddDatabaseDeveloperPageExceptionFilter();
//Sets up the default Asp.net core Identity Screens - Use Identity Scaffolding to override defaults
services.AddDefaultIdentity<ApplicationUser>( options =>
{
***OMITED***
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<DataContext>();
//Associates the User to Context with Identity
services.AddIdentityServer().AddApiAuthorization<ApplicationUser, DataContext>( options =>
{
options.IdentityResources["openid"].UserClaims.Add(JwtClaimTypes.Role);
options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Role);
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove(JwtClaimTypes.Role);
//Adds authentication handler
services.AddAuthentication().AddIdentityServerJwt();
services.AddCors(options =>
{
options.AddDefaultPolicy(
policy =>
{
policy.WithOrigins("https://www.siteName.com",
"https://siteName.com",
"https://siteName.azurewebsites.net",
"https://localhost:44395"
);
});
});
services.AddHttpContextAccessor();
services.AddControllersWithViews();
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//AutoMigrates data
dataContext.Database.Migrate();
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseSerilogIngestion();
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
//endpoints.MapFallbackToFile("index.html");
endpoints.MapFallbackToPage("/_Host");
});
}
Server - Pages/_Host.cshtml
@page
@using Microsoft.AspNetCore.Components.Web;
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />
<base href="/" />
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
</script>
</head>
<body>
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/Blazor-Analytics/blazor-analytics.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
</body>
</html>
Client - Program.cs
public class Program
{
public static async Task Main(string[] args)
{
//Serilog
var levelSwitch = new LoggingLevelSwitch();
Serilog.Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitch)
.Enrich.WithProperty("InstanceId", Guid.NewGuid())
.Enrich.FromLogContext()
.WriteTo.BrowserHttp(controlLevelSwitch: levelSwitch)
.CreateLogger();
Serilog.Log.ForContext<Program>().Information("Client has started");
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddAuthorizationCore();
builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<RolesClaimsPrincipalFactory>();
builder.Services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog(dispose: true);
});
var baseAddress = new Uri($"{builder.HostEnvironment.BaseAddress}api/");
builder.Services.AddHttpClient("ApplicationName.ServerAPI", client => client.BaseAddress = baseAddress)
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
//Add Public Client
builder.Services.AddHttpClient<PublicClient>(client => client.BaseAddress = baseAddress);
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp =>
sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("BBQFriend.ServerAPI"));
builder.RootComponents.Add<HeadOutlet>("head::after");
await builder.Build().RunAsync();
}
}