MarimerLLC / csla

A home for your business logic in any .NET application.
https://cslanet.com
MIT License
1.27k stars 406 forks source link

Authorization and MAUI Blazor Hybrid #3253

Open MarkOverstreet opened 1 year ago

MarkOverstreet commented 1 year ago

Describe the bug I am trying to create a simple MAUI Blazor Hybrid app using CSLA with authentication and getting an error when the app starts. I used your example code from the MAUI app and it fails when trying to find the Csla.ApplicationContext class.

Version and Platform CSLA version: 6.2.2 OS: Windows 10 Platform: MAUI Blazor Hybrid

Code that Fails

            var provider = builder.Services.BuildServiceProvider();
            App.ApplicationContext = provider.GetRequiredService<ApplicationContext>();  // this line fails

Stack Trace or Exception Detail System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider' while attempting to activate 'Csla.Blazor.WebAssembly.ApplicationContextManager'.'

rockfordlhotka commented 1 year ago

It is saying that there's no type mapped to the Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider service type.

The CSLA ApplicationContext type needs access to an AuthenticationStateProvider instance, because that's where Blazor stores the current user identity.

The AuthenticationStateProvider service may be mapped in various ways - I'm not sure there's an automatic mapping for a MAUI Blazor Hybrid app type.

MarkOverstreet commented 1 year ago

After some more analysis I am not sure what to do so let me describe the simple app I am trying to build to prove that CSLA and MAUI Hybrid will work for us. I am trying to build a login component (ex: LoginComponent.razor) that is in a shared class library that can then be used across Server, WASM and MAUI Blazor Hybrid. I currently have it working in WASM but the same component generates the error above in MAUI Blazor Hybrid and I believe it is because we built the shared class library referencing the Csla.Blazor.WebAssembly.

So the question is how in CSLA can I create a shared LoginComponent so that we can use the same login screen and authentication across all 3 platforms?

rockfordlhotka commented 1 year ago

It might be your component, but the error is saying that in your app startup you aren't mapping a type to the AuthenticationStateProvider service.

So it is also quite possible that your component is fine, and would work if a type was mapped to that service during app startup. Something like this:

  services. AddScoped<AuthenticationStateProvider, yourtype>();
rockfordlhotka commented 1 year ago

(I've been leaving this as an Issue instead of Discussion b/c maybe there's something we need to add to CSLA to better support the MAUI hybrid scenario)

MarkOverstreet commented 1 year ago

We looked into this further and were able to get a working solution; however, we may not have done it correctly and need your advice. We created a sample project that demonstrates authorization in a WASM project, a Server project and a BlazorHybridMAUI project where we are using a shared UI library and you can see the code here... https://github.com/IgnyteSoftware/CslaBlazorMaui

Essentially, in order to share authorization with all three project types using a shared UI library, we had to add a reference to Csla.Blazor.WebAssembly in each project because this had the CslaAuthorizationStateProvider. Obviously, we can have this reference in the WASM project, but it seems that we shouldn't have it in the Server and MAUI projects. We noticed a commit message (https://github.com/MarimerLLC/csla/commit/feb1739f234934c1228ba61df746e691b541f7be) that indicated you moved the CslaAuthorizationStateProvider to only build in the WASM project. Why? Is there a reason we can't share this between all 3 projects?

The code below demonstrates a workaround of sorts. Maybe CSLA needs to add a configuration method specific to BlazorHybridMAUI (ex .AddBlazorMaui).

// The following lines were required in the MAUI and Server projects to share the csla authentication state provider
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<CslaAuthenticationStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CslaAuthenticationStateProvider>());

// The following lines were required for Maui only, Server is configured differently as you have shown in your standard sample project
builder.Services.AddTransient<HttpClient>();
builder.Services.AddCsla(o => o
  .AddBlazorWebAssembly()  // This was required in the MAUI project to hookup the ViewModel provided by CSLA 
  .DataPortal(dpo => dpo
    .UseHttpProxy(options => options.DataPortalUrl = "https://localhost:7078/api/DataPortal")));
rockfordlhotka commented 1 year ago

iirc, the change was made because Blazor server provides an ASP.NET Core specific provider that works in the server scenario.

At the time I wasn't thinking about MAUI hybrid - which might benefit from the same CSLA type as wasm.