Nesco.SignalRUserManagement.Client.Authorization 1.0.11

dotnet add package Nesco.SignalRUserManagement.Client.Authorization --version 1.0.11
                    
NuGet\Install-Package Nesco.SignalRUserManagement.Client.Authorization -Version 1.0.11
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nesco.SignalRUserManagement.Client.Authorization" Version="1.0.11" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nesco.SignalRUserManagement.Client.Authorization" Version="1.0.11" />
                    
Directory.Packages.props
<PackageReference Include="Nesco.SignalRUserManagement.Client.Authorization" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nesco.SignalRUserManagement.Client.Authorization --version 1.0.11
                    
#r "nuget: Nesco.SignalRUserManagement.Client.Authorization, 1.0.11"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nesco.SignalRUserManagement.Client.Authorization@1.0.11
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nesco.SignalRUserManagement.Client.Authorization&version=1.0.11
                    
Install as a Cake Addin
#tool nuget:?package=Nesco.SignalRUserManagement.Client.Authorization&version=1.0.11
                    
Install as a Cake Tool

Nesco.SignalRUserManagement.Client.Authorization

Client-side JWT authentication library for Blazor WebAssembly and MAUI applications using SignalR User Management.

Installation

dotnet add package Nesco.SignalRUserManagement.Client.Authorization

Setup

1. Configure HttpClient

Register an HttpClient with the base address pointing to your server before adding auth services:

// Program.cs
var builder = WebAssemblyHostBuilder.CreateDefault(args);

// Configure HttpClient with your server URL
builder.Services.AddScoped(sp => new HttpClient
{
    BaseAddress = new Uri("http://localhost:5000")
});

2. Add Auth Services

Blazor WebAssembly (Default - uses localStorage)
using Nesco.SignalRUserManagement.Client.Authorization.Extensions;

// Add authentication services (uses localStorage by default)
builder.Services.AddSignalRClientAuth();
Blazor Server (uses ProtectedSessionStorage)

For Blazor Server apps, use encrypted session storage:

using Nesco.SignalRUserManagement.Client.Authorization.Extensions;

// Add authentication services with protected session storage
builder.Services.AddSignalRClientAuth<ProtectedSessionAuthTokenStorage>();

You'll need to implement ProtectedSessionAuthTokenStorage:

using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Nesco.SignalRUserManagement.Client.Authorization.Services;

public class ProtectedSessionAuthTokenStorage : IAuthTokenStorage
{
    private readonly ProtectedSessionStorage _sessionStorage;
    private const string TokenKey = "SignalRAuth.Token";
    private const string UserIdKey = "SignalRAuth.UserId";
    private const string EmailKey = "SignalRAuth.Email";

    public ProtectedSessionAuthTokenStorage(ProtectedSessionStorage sessionStorage)
    {
        _sessionStorage = sessionStorage;
    }

    public async Task<string?> GetTokenAsync()
    {
        try
        {
            var result = await _sessionStorage.GetAsync<string>(TokenKey);
            return result.Success ? result.Value : null;
        }
        catch { return null; }
    }

    public async Task<string?> GetUserIdAsync()
    {
        try
        {
            var result = await _sessionStorage.GetAsync<string>(UserIdKey);
            return result.Success ? result.Value : null;
        }
        catch { return null; }
    }

    public async Task<string?> GetEmailAsync()
    {
        try
        {
            var result = await _sessionStorage.GetAsync<string>(EmailKey);
            return result.Success ? result.Value : null;
        }
        catch { return null; }
    }

    public async Task SaveAsync(string? token, string userId, string email)
    {
        try
        {
            if (!string.IsNullOrEmpty(token))
            {
                await _sessionStorage.SetAsync(TokenKey, token);
                await _sessionStorage.SetAsync(UserIdKey, userId);
                await _sessionStorage.SetAsync(EmailKey, email);
            }
        }
        catch { }
    }

    public async Task ClearAsync()
    {
        try
        {
            await _sessionStorage.DeleteAsync(TokenKey);
            await _sessionStorage.DeleteAsync(UserIdKey);
            await _sessionStorage.DeleteAsync(EmailKey);
        }
        catch { }
    }
}
MAUI or Custom Storage

For MAUI apps or other platforms, implement IAuthTokenStorage and register it:

using Nesco.SignalRUserManagement.Client.Authorization.Extensions;

// Add authentication services with custom storage
builder.Services.AddSignalRClientAuth<PreferencesAuthTokenStorage>();

3. Configure App.razor

Wrap your app with CascadingAuthenticationState:

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
            <FocusOnNavigate RouteData="@routeData" Selector="h1" />
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

Custom Token Storage

The library uses an IAuthTokenStorage interface for persisting authentication data. This allows you to implement custom storage mechanisms for different platforms.

IAuthTokenStorage Interface

public interface IAuthTokenStorage
{
    Task<string?> GetTokenAsync();
    Task<string?> GetUserIdAsync();
    Task<string?> GetEmailAsync();
    Task SaveAsync(string? token, string userId, string email);
    Task ClearAsync();
}

Built-in Implementation

  • LocalStorageAuthTokenStorage - Default for Blazor WebAssembly, uses browser localStorage via JSInterop
  • ProtectedSessionAuthTokenStorage - For Blazor Server, uses encrypted session storage (see example above)

MAUI Example (using Preferences)

using Nesco.SignalRUserManagement.Client.Authorization.Services;

public class PreferencesAuthTokenStorage : IAuthTokenStorage
{
    private const string TokenKey = "authToken";
    private const string UserIdKey = "userId";
    private const string EmailKey = "userEmail";

    public Task<string?> GetTokenAsync() =>
        Task.FromResult(Preferences.Default.Get<string?>(TokenKey, null));

    public Task<string?> GetUserIdAsync() =>
        Task.FromResult(Preferences.Default.Get<string?>(UserIdKey, null));

    public Task<string?> GetEmailAsync() =>
        Task.FromResult(Preferences.Default.Get<string?>(EmailKey, null));

    public Task SaveAsync(string? token, string userId, string email)
    {
        Preferences.Default.Set(TokenKey, token ?? "");
        Preferences.Default.Set(UserIdKey, userId);
        Preferences.Default.Set(EmailKey, email);
        return Task.CompletedTask;
    }

    public Task ClearAsync()
    {
        Preferences.Default.Remove(TokenKey);
        Preferences.Default.Remove(UserIdKey);
        Preferences.Default.Remove(EmailKey);
        return Task.CompletedTask;
    }
}

Then register it in MauiProgram.cs:

builder.Services.AddSignalRClientAuth<PreferencesAuthTokenStorage>();

SecureStorage Example (for sensitive data)

public class SecureStorageAuthTokenStorage : IAuthTokenStorage
{
    private const string TokenKey = "authToken";
    private const string UserIdKey = "userId";
    private const string EmailKey = "userEmail";

    public async Task<string?> GetTokenAsync() =>
        await SecureStorage.Default.GetAsync(TokenKey);

    public async Task<string?> GetUserIdAsync() =>
        await SecureStorage.Default.GetAsync(UserIdKey);

    public async Task<string?> GetEmailAsync() =>
        await SecureStorage.Default.GetAsync(EmailKey);

    public async Task SaveAsync(string? token, string userId, string email)
    {
        if (!string.IsNullOrEmpty(token))
            await SecureStorage.Default.SetAsync(TokenKey, token);
        await SecureStorage.Default.SetAsync(UserIdKey, userId);
        await SecureStorage.Default.SetAsync(EmailKey, email);
    }

    public Task ClearAsync()
    {
        SecureStorage.Default.Remove(TokenKey);
        SecureStorage.Default.Remove(UserIdKey);
        SecureStorage.Default.Remove(EmailKey);
        return Task.CompletedTask;
    }
}

Usage

Login Page

@page "/login"
@using Nesco.SignalRUserManagement.Client.Authorization.Services
@inject AuthService AuthService
@inject NavigationManager Navigation

<EditForm Model="loginModel" OnValidSubmit="HandleLogin">
    <div>
        <label>Email</label>
        <InputText @bind-Value="loginModel.Email" />
    </div>
    <div>
        <label>Password</label>
        <InputText @bind-Value="loginModel.Password" type="password" />
    </div>
    <button type="submit">Login</button>
</EditForm>

@if (!string.IsNullOrEmpty(errorMessage))
{
    <div class="alert alert-danger">@errorMessage</div>
}

@code {
    private LoginModel loginModel = new();
    private string? errorMessage;

    private async Task HandleLogin()
    {
        var result = await AuthService.LoginAsync(loginModel.Email, loginModel.Password);

        if (result.Success)
        {
            Navigation.NavigateTo("/");
        }
        else
        {
            errorMessage = result.Error ?? "Login failed";
        }
    }

    public class LoginModel
    {
        public string Email { get; set; } = string.Empty;
        public string Password { get; set; } = string.Empty;
    }
}

Protected Pages

@page "/"
@using Nesco.SignalRUserManagement.Client.Authorization.Services
@inject AuthService AuthService
@inject NavigationManager Navigation

@if (!AuthService.IsAuthenticated)
{
    <p>Redirecting to login...</p>
}
else
{
    <h1>Welcome, @AuthService.CurrentUser.Identity?.Name</h1>
    <button @onclick="HandleLogout">Logout</button>
}

@code {
    protected override async Task OnInitializedAsync()
    {
        await AuthService.CheckAuthAsync();

        if (!AuthService.IsAuthenticated)
        {
            Navigation.NavigateTo("/login");
        }
    }

    private async Task HandleLogout()
    {
        await AuthService.LogoutAsync();
        Navigation.NavigateTo("/login");
    }
}

Using with SignalR

Pass the access token to your SignalR connection:

await connectionClient.StartAsync(
    "http://localhost:5000/hubs/usermanagement",
    () => Task.FromResult(AuthService.AccessToken!));

API Reference

AuthService

Property Type Description
CurrentUser ClaimsPrincipal The current authenticated user
IsAuthenticated bool Whether the user is authenticated
AccessToken string? The JWT access token
UserId string? The current user's ID
Method Returns Description
LoginAsync(email, password) Task<LoginResult> Authenticate with email and password
LogoutAsync() Task Clear authentication state
CheckAuthAsync() Task<bool> Initialize from storage and check auth status
Event Description
AuthStateChanged Raised when authentication state changes

LoginResult

Property Type Description
Success bool Whether login succeeded
Error string? Error message if login failed

IAuthTokenStorage

Method Returns Description
GetTokenAsync() Task<string?> Get stored token
GetUserIdAsync() Task<string?> Get stored user ID
GetEmailAsync() Task<string?> Get stored email
SaveAsync(token, userId, email) Task Save auth data
ClearAsync() Task Clear all auth data

Features

  • JWT token storage with pluggable storage backends
  • Default localStorage support for Blazor WebAssembly
  • Easy to implement custom storage for MAUI (Preferences, SecureStorage)
  • Automatic token restoration on app startup
  • Integration with Blazor's AuthenticationStateProvider
  • Works with AuthorizeView and [Authorize] attributes

Server Requirements

This library expects your server to have an auth endpoint at api/auth/login that:

  • Accepts POST requests with JSON body: { "email": "...", "password": "..." }
  • Returns JSON on success: { "userId": "...", "email": "...", "token": "..." }

Use the companion library Nesco.SignalRUserManagement.Server.Authorization for a ready-made implementation.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.11 432 12/10/2025
1.0.9 349 12/8/2025
1.0.8 213 12/7/2025
1.0.6 135 12/6/2025