Skip to content

This sample demonstrates how to build .NET MAUI Blazor Hybrid and Web Apps that share UI and also provides authentication. It uses ASP.NET Core Identity local accounts but you can use this pattern for any authentication provider.

Notifications You must be signed in to change notification settings

hgirma/MauiHybridAuth

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

.NET MAUI Blazor Hybrid and Web App Auth Sample

This sample demonstrates how to build .NET MAUI Blazor Hybrid and Web Apps that shares common UI and also provides authentication. It uses ASP.NET Core Identity local accounts but you can use this pattern for any authentication provider you need to call from a MAUI Blazor Hybrid client.

Running the sample

  1. Clone the repository.
  2. Make sure you have .NET 9 installed and the MAUI workload.
  3. Open the solution in Visual Studio 2022 or VS Code with the .NET MAUI extension installed.
  4. Set the MauiHybridAuth MAUI project as the startup project.
  5. Start the MauiHybridAuth.Web project without debugging (in Visual Studio right-click on the project and select "Debug -> Start without Debugging").
  6. Register a user in the Blazor Web app UI or navigate to https://localhost:7157/swagger in your browser to pull up the identity endpoints and register a user using the /Register endpoint.
  7. Start (F5) the MauiHybridAuth MAUI project. You can set the debug target to Windows, or an Android device or emulator.
  8. Notice you can only see the Home and Login pages.
  9. Log in with the user you registered.
  10. Notice you can now see the shared Counter and Weather pages.
  11. Log out and notice you can only see the Home and Login pages again.
  12. Navigate the web app to https://localhost:7157/ and the app will behave the same.

Tour of the important parts

Shared UI

The shared UI is in the MauiHybridAuth.Shared project. This project contains the Razor components that are shared between the MAUI and Blazor Web projects (Home, Counter and Weather pages). The Counter.razor and Weather.razor pages are protected by the [Authorize] attribute so you cannot navigate to them unless you are logged in.

@page "/counter"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]

Routing in the MAUI & Blazor apps

The Routes.razor uses the AuthorizeRouteView to route users appropriately based on their authentication status. If a user is not authenticated, they are redirected to the Login page.

<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)">
    <Authorizing>
        Authorizing...
    </Authorizing>
    <NotAuthorized>
        <Login />
    </NotAuthorized>
</AuthorizeRouteView>  

The NavManu.razor components contain the navigation menu that uses AuthorizationView to show/hide links based on the user's authentication status.

<AuthorizeView>
    <NotAuthorized>
       <!-- Navlinks that display when not logged in -->    
    </NotAuthorized>
    <Authorized>
        <!-- Navlinks that display when logged in -->    
    </Authorized>               
</AuthorizeView>

Setting up the server

The Blazor Web app contains all the pages and uses the SignInManager framework class to manage logins and users. All of this is generated automatically when you create a Blazor Web project and select to use Authentication with Individual accounts. In order for the MAUI client (or any external client) to authenticate, the ASP.NET Identity endpoints need to be exposed. In the Program.cs file this is set up with the call to AddIdentityEnpoints and MapIdentityApi. NOTE: You'll need to remove the generated call to .AddIdentityCookies on .AddAuthentication. It is not necessary when calling .AddIdentityEndpoints and will result in an error.

// Add Auth services used by the Web app
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = IdentityConstants.ApplicationScheme;
    options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
});
 
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

//Needed for external clients to log in
builder.Services.AddIdentityApiEndpoints<ApplicationUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();

//Register needed elements for authentication:
builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();

var app = builder.Build();

//Needed for external clients to log in
app.MapIdentityApi<ApplicationUser>();

Logging in from the MAUI client

The Login.razor page is where the user logs in. The Login page injects the ICustomAuthenticationStateProvider and uses the AuthenticationStateProvider to authenticate the user and redirect them to the home page if successful. When the state changes, the AuthorizeView reacts and will show the appropriate pages/links based on the user's authentication status.

//Called on valid submit
private async Task LoginUser()
{
    await AuthStateProvider.LogInAsync(LoginModel);

    if (AuthStateProvider.LoginStatus != LoginStatus.Success)
    {
        //Show error message
        loginFailureHidden = false;
        return;
    }        
        
    Navigation.NavigateTo(""); //Root URL
}

NOTE: This sample only implements Login and Logout pages on the MAUI client but you could build Register and other management pages against the exposed endpoints for more functionality. For more information on identity endpoints see How to use Identity to secure a Web API backend for SPAs

MauiAuthenticationStateProvider

The ICustomAuthenticationStateProvider interface is implemented by the MauiAuthenticationStateProvider class in the MauiHybridAuth MAUI project. This class is responsible for managing the user's authentication state and providing the AuthenticationState to the app. The MauiAuthenticationStateProvider class uses the HttpClient to make requests to the server to authenticate the user. See the official documentation for more information on ASP.NET Core Blazor Hybrid authentication and authorization.

public interface ICustomAuthenticationStateProvider 
{
    public LoginStatus LoginStatus { get; set; }
    Task<AuthenticationState> GetAuthenticationStateAsync();
    Task LogInAsync(LoginModel loginModel);
    void Logout();
}

This class also handles calling localhost via the emulators and simulators for easy testing. See the official documentation for information on what you need to do to be able to call local services from emulators and simulators. This class could also use the SecureStorage API to store the user's token securely on the device, or handle any other platform specific functionality if needed.

MAUI MauiProgram.cs

The MAUI project's MauiProgram.cs file is where the MauiAuthenticationStateProvider is registered with the DI container. It also needs to register the Authorization core components where things like AuthorizeView are defined.

 // This is the core functionality
builder.Services.AddAuthorizationCore();
// This is our custom provider
builder.Services.AddScoped<ICustomAuthenticationStateProvider, MauiAuthenticationStateProvider>();
// Use our custom provider when the app needs an AuthenticationStateProvider
builder.Services.AddScoped<AuthenticationStateProvider>(s 
    => (MauiAuthenticationStateProvider)s.GetRequiredService<ICustomAuthenticationStateProvider>());

About

This sample demonstrates how to build .NET MAUI Blazor Hybrid and Web Apps that share UI and also provides authentication. It uses ASP.NET Core Identity local accounts but you can use this pattern for any authentication provider.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • HTML 60.5%
  • C# 29.6%
  • CSS 9.9%