Table of Contents


Secure JWT Authentication in .NET MAUI

1 . Install .NET MAUI Tools in Visual Studio

Go to Tools > Get Tools and Features.

Under Mobile Development with .NET, check ✅ .NET MAUI.

Click Install, and restart Visual Studio after installation.

2. Create a New .NET MAUI Project

In your existing solution, right-click on the Solution → Add > New Project.

Search for “.NET MAUI App” and select it.

Enter a name and choose your target version.

After the project is created, you’ll see the project structure.

3. Project Structure

Once created, you'll see:

├── App.xaml / App.xaml.cs => App lifecycle

├── MainPage.xaml / MainPage.xaml.cs => Home screen

├── Platforms/ => Platform-specific configs

├── MauiProgram.cs => Dependency Injection

├── Resources/ => Images, styles, fonts

├── Views/ => Create LoginPage.xaml here

├── Services/ => Create ApiService.cs, AuthHandler.cs here

4. Create the Login Page

A : Right-click the Views folder → Add → New Item → Content Page (XAML) → Name it LoginPage.xaml.

B: Open AppShell.xaml and register the route:


<Shell ... xmlns:views="clr-namespace:JwtAuthMauiApp.Views">
    <ShellContent Route="LoginPage" ContentTemplate="{DataTemplate views:LoginPage}" />
</Shell>
									

5. Design and Code the Login Page

Change code and and according need

This is my login page code


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="JwtAuthMauiApp.Views.LoginPage"
             Title="Login"
             BackgroundColor="White">
    <ScrollView>
        <VerticalStackLayout Padding="30" Spacing="25"
                             VerticalOptions="Center">
            <Label Text="Welcome Back "
                   FontSize="24"
                   FontAttributes="Bold"
                   HorizontalOptions="Center" />
            <Label Text="Sign in to continue"
                   FontSize="16"
                   TextColor="Gray"
                   HorizontalOptions="Center" />
            <Entry x:Name="EmailEntry"
                   Placeholder="Email or Username"
                   Keyboard="Email"
                   ClearButtonVisibility="WhileEditing" />
            <Entry x:Name="PasswordEntry"
                   Placeholder="Password"
                   IsPassword="True"
                   ClearButtonVisibility="WhileEditing" />
            <Button Text="Login"
                    Clicked="OnLoginClicked"
                    BackgroundColor="#007AFF"
                    TextColor="White"
                    CornerRadius="8"
                    FontAttributes="Bold"
                    HeightRequest="45" />
            <Label x:Name="ErrorLabel"
                   TextColor="Red"
                   IsVisible="False"
                   FontSize="14"
                   HorizontalOptions="Center"
                   HorizontalTextAlignment="Center" />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>
- - - - - - - -  - - - - - - - - - - - - - - -  -
						
						

In LoginPage.xaml.cs, add login logic:


using var client = new HttpClient();
client.BaseAddress = new Uri("https://your-api-url.com"); 
var response = await client.PostAsync("/api/auth/login", content);						
						

Set url pass and called you api

If successful, store the token:


await SecureStorage.SetAsync("auth_token", token);						
						

6. Configure App Launch Logic

On Login page or any other page add this code for view page


x:Class="JwtAuthMauiApp.Views.LoginPage">
						

After run project first called app.xaml

Set inside this called for default run login page

Use this code in app.xaml.cs


public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }
    protected override Window CreateWindow(IActivationState? activationState)
    {
        return new Window(new AppShell());
    }
    protected override void OnStart()
    {
        base.OnStart();
        Task.Run(async () =>
        {
            var token = await SecureStorage.GetAsync("auth_token");
            MainThread.BeginInvokeOnMainThread(() =>
            {
                if (!string.IsNullOrEmpty(token))
                    Shell.Current.GoToAsync("//MainPage");
                else
                    Shell.Current.GoToAsync("//LoginPage");
            });
        });
    }}
						

Thase code is if token have then show main page else login page

7. Add Logout Support

On your logout button’s click event:


SecureStorage.Remove("auth_token");
await Shell.Current.GoToAsync("//LoginPage");
						

8. Create AuthHandler to Attach Token Automatically

Create a folder called Handler, add AuthHandler.cs:


public class AuthHandler : DelegatingHandler
{
    protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await SecureStorage.GetAsync("auth_token");
        if (!string.IsNullOrEmpty(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
        var response = await base.SendAsync(request, cancellationToken);
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            await SecureStorage.SetAsync("auth_token", string.Empty);
            MainThread.BeginInvokeOnMainThread(async () =>
            {
                await Shell.Current.GoToAsync("//LoginPage");
            });
        }
        return response;
    }
}
						

9. Register AuthHandler in MauiProgram.cs

In MauiProgram.cs, add:


builder.Services.AddTransient<AuthHandler>();
builder.Services.AddHttpClient("apiClient").AddHttpMessageHandler<AuthHandler>();
						

If you get a missing dependency error, install:


Microsoft.Extensions.Http
						

For set parameter according API.


var email = EmailEntry.Text;
var password = PasswordEntry.Text;
var loginData = new
{
    username = EmailEntry.Text, 
    password = PasswordEntry.Text
};
var loginDataJson = JsonSerializer.Serialize(loginData);
var content = new StringContent(loginDataJson, Encoding.UTF8, "application/json");

						

According you api set parameter and convert into json

For add menu in side bar add this code in AppShell.xaml


<ShellContent  Title="Login" Route="LoginPage" ContentTemplate="{DataTemplate views:LoginPage}" />					
							

Login Flow

Forgot Password

For add this in LoginPage.xaml:


<!--Forgot Password link-->
<Button Text="Forgot Password?"
        Clicked="OnForgotPasswordClicked"
        BackgroundColor="Transparent"
        TextColor="#007AFF"
        FontSize="14"
        HorizontalOptions="End"
        Padding="0"
        BorderWidth="0" />
						

On OnForgotPasswordClicked add this code in LoginPage.xaml.cs


 private async void OnForgotPasswordClicked(object sender, EventArgs e)
{
	await Shell.Current.GoToAsync(nameof(ForgotPasswordPage));
}						
						

First We need 2 API

1. Generate Token For Forgot Password

2. Reset Password

1.


[HttpPost("forgotpassword")]
    public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordRequest request)
    {
        var user = await _userManager.FindByEmailAsync(request.Email);
        if (user == null)
            return BadRequest("User not found");
        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
        return Ok(new { token });
    }
							
							

2.


[HttpPost("resetpassword")]
 public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordRequest request)
 {
     var user = await _userManager.FindByEmailAsync(request.Email);
     if (user == null)
         return BadRequest("User not found");
     var result = await _userManager.ResetPasswordAsync(user, request.Token, request.NewPassword);
     if (result.Succeeded)
         return Ok("Password reset successful");

     return BadRequest(result.Errors);
 }
							
							

Check in Swagger it Work Proper not Then If Yes Then Move to next step.

Right Click on Project add new folder Model, Views On Views Add 2 Page.

1. Forgot Password

2. Reset Password

1. Forgot Password

On AppShell.xaml.cs


Routing.RegisterRoute("ForgotPasswordPage", typeof(ForgotPasswordPage));
Routing.RegisterRoute("ResetPasswordPage", typeof(ResetPasswordPage));								
								

Add This code According S.S

On Forgot Password Page Add Email Textbox and button

If Email is valid and user Find Then Return Token and redirect Reset Password Page


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="JwtAuthMauiApp.Views.ForgotPasswordPage"
             Title="ForgotPasswordPage">
    <ScrollView>
        <VerticalStackLayout Padding="30" Spacing="20" VerticalOptions="Center">

            <Label Text="Forgot Your Password?" 
                   FontSize="24" 
                   FontAttributes="Bold"
                   HorizontalOptions="Center" />

            <Label Text="Enter your email to receive a reset link."
                   FontSize="14"
                   HorizontalOptions="Center"
                   TextColor="Gray" />

            <Entry x:Name="EmailEntry"
                   Placeholder="Email"
                   Keyboard="Email"
                   FontSize="16"
                   BackgroundColor="#f0f0f0"
                   HeightRequest="50"
                   Margin="0,20,0,0" />

            <Button Text="Send Reset Link"
                    Clicked="OnSendClicked"
                    BackgroundColor="#007AFF"
                    TextColor="White"
                    CornerRadius="10"
                    HeightRequest="50" />

            <Label x:Name="ResponseLabel"
                   Text=""
                   TextColor="Red"
                   FontSize="14"
                   IsVisible="False"
                   HorizontalOptions="Center"
                   LineBreakMode="WordWrap"
                   Margin="0,10,0,0"/>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>
								

Page Code Of Forgot Password

On ForgotPasswordPage.xaml.cs

Add click OnSendClicked Event Code


private async void OnSendClicked(object sender, EventArgs e)
{
	var email = EmailEntry.Text;
	if (email == null) {
		ResponseLabel.Text = "Invalid credentials";
		ResponseLabel.IsVisible = true;
	}
	var data = new ForgotPasswordRequest { Email = email };
	var json = JsonSerializer.Serialize(data);
    var content = new StringContent(json, Encoding.UTF8, "application/json");
	using var client = new HttpClient { BaseAddress = new Uri("https://localhost:44302") };
	var response = await client.PostAsync("/api/forgotpassword", content);
	if (response.IsSuccessStatusCode)
	{
		var tokenJson = await response.Content.ReadAsStringAsync();
		var result = JsonSerializer.Deserialize<Dictionary<string, string>>(tokenJson);
		var token = result["token"];
		var encodedToken = Uri.EscapeDataString(token);
		await Shell.Current.GoToAsync($"ResetPasswordPage?email={email}&token={encodedToken}");
	}
	else
	{
		ResponseLabel.Text = "Error sending reset request.";
		ResponseLabel.IsVisible = true;
	}
}
								
								

This code return Token And Redirect reset Password Page

2. Reset Password Page:

Add 2 textbox And Button

Password and confirm Password and submit button


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="JwtAuthMauiApp.Views.ResetPasswordPage"
             Title="ResetPasswordPage">
    <VerticalStackLayout Padding="30" Spacing="20">
        <Label Text="Enter New Password" FontSize="18" />
        <Entry x:Name="NewPasswordEntry" Placeholder="New Password" IsPassword="True" />
        <Entry x:Name="ConfirmPasswordEntry" Placeholder="Confirm Password" IsPassword="True" />
        <Button Text="Reset Password" Clicked="OnResetPasswordClicked" />
        <Label x:Name="ResponseLabel" Text="" TextColor="Red" IsVisible="False" />
    </VerticalStackLayout>
</ContentPage>
								
								

Code Of Design Side

On ResetPassword.xaml.cs Page add this code according s.s


[QueryProperty(nameof(Email), "email")]
[QueryProperty(nameof(Token), "token")]
 public string Email { get; set; }
 public string Token { get; set; }
										

On Click OnResetPasswordClicked add this code

Which is reset password if successfully reset then so alert msg and redirect login page else pass error msg


private async void OnResetPasswordClicked(object sender, EventArgs e)
{
    var newPassword = NewPasswordEntry.Text;
    var confirmPassword = ConfirmPasswordEntry.Text;
    if (string.IsNullOrWhiteSpace(newPassword) || newPassword != confirmPassword)
    {
        ResponseLabel.Text = "Passwords do not match.";
        ResponseLabel.IsVisible = true;
        return;
    }
    var resetData = new ResetPasswordRequest
    {
        Email = Email,
        Token = Token,
        NewPassword = newPassword
    };
    var json = JsonSerializer.Serialize(resetData, new JsonSerializerOptions
    {
        PropertyNamingPolicy = null
    });
    var content = new StringContent(json, Encoding.UTF8, "application/json");
    using var client = new HttpClient { BaseAddress = new Uri("https://localhost:44302") };
    var response = await client.PostAsync("/api/resetpassword", content);
    if (response.IsSuccessStatusCode)
    {
        await DisplayAlert("Success", "Password reset successfully.", "OK");
        await Shell.Current.GoToAsync("///LoginPage");
    }
    else
    {
        ResponseLabel.Text = "Failed to reset password.";
        ResponseLabel.IsVisible = true;
    }
}
								

Ready to Build Something Amazing?

Get in touch with Prishusoft – your trusted partner for custom software development. Whether you need a powerful web application or a sleek mobile app, our expert team is here to turn your ideas into reality.

image