Reputation: 1200
Is there a way to use the default Authentication (.NET 5 / Identity-Server) with Blazor ServerSide and Blazor WebAssembly in a hybrid manner? I want to have a Blazor Project, which can be switched between ClientSide (WebAssembly) and ServerSide, to keep the Client the same, I want to use the WebAPI on ClientSide and ServerSide. I started with ServerSide (better debugging and better performance for tables *) and maybe switch to ClientSide later (if the performance will be better).
*Please do not start discussing about what is better or if YOU maybe have a good performance with WebAssembly, then you don't have many nested components like component based tables.
I did test so many combinations, but can't get it to work, but let's start with the basic:
Startup.cs (Server)
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer().AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication().AddIdentityServerJwt();
// SERVER SIDE SUPPORT
services.AddServerSideBlazor();
services.AddApiAuthorization();
if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
{
services.AddScoped(s =>
{
var uriHelper = s.GetRequiredService<NavigationManager>();
return new HttpClient
{
BaseAddress = new System.Uri(uriHelper.BaseUri)
};
});
}
// --
services.AddControllersWithViews();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
// SERVER-SIDE SUPPORT
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host"); //endpoints.MapFallbackToFile("index.html");
// --
});
}
Program.cs (Client)
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("BlazorSwitchWithSec2.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorSwitchWithSec2.ServerAPI"));
builder.Services.AddApiAuthorization();
await builder.Build().RunAsync();
}
_Host.cshtml (Server)
@page "/"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
@if (Request.QueryString.Value.ToLower().Contains("mode=client"))
{
<title>WebApp (CE)</title>
}
else
{
<title>WebApp (SE)</title>
}
<base href="~/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
</head>
<body>
@if (Request.QueryString.Value.ToLower().Contains("mode=client"))
{
<component type="typeof(BlazorSwitchWithSec2.Client.App)" render-mode="WebAssemblyPrerendered" />
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
}
else
{
<component type="typeof(BlazorSwitchWithSec2.Client.App)" render-mode="Server" />
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
<script src="_framework/blazor.server.js"></script>
}
</body>
</html>
If I try to register or login via WebAssembly or Server, I get this error:
Unable to cast object of type 'Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider' to type 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.IRemoteAuthenticationService`1[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState]'
Maybe also this helps: I tried something different like using the services from the client, one combination seemed to work with controllers without [Authorize] attribute, but with it, I got a error like Unable to parse ...
- the result was a login url (this is why the parsing didn't work). I can't post the code, because I tested too much and the project isn't working anymore.
Without any Authentication everything works and I could send a token to the WebAPI to create my own login system. But I want to try the default one.
Upvotes: 2
Views: 1484
Reputation: 31
I don’t like the approach of BlazorServer referencing BlazorWasm. I prefer to have both BlazorServer and BlazorWasm projects reference a dll(rdl) which contains the App, makes it a bit more clear that both are using a shared codebase and it avoids BlazorServer pulling in unnecessary dependencies from BlazorWasm. you can check this link https://github.com/mostafaefcih/BlazorDaulMode or check blazor getting started course on plaursight
Upvotes: 1
Reputation: 798
These Blazor Train videos from Carl Franklin may help you.
Blazor Synchronicity; Develop Server & WASM Apps Simultaneously
https://www.youtube.com/watch?v=SkYQDPXw__c
Blazor Synchronicity 5.0
https://www.youtube.com/watch?v=fHzIWOfmqzg
Upvotes: 0