YGAuthentication 1.2.5

There is a newer version of this package available.
See the version list below for details.
dotnet add package YGAuthentication --version 1.2.5
                    
NuGet\Install-Package YGAuthentication -Version 1.2.5
                    
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="YGAuthentication" Version="1.2.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="YGAuthentication" Version="1.2.5" />
                    
Directory.Packages.props
<PackageReference Include="YGAuthentication" />
                    
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 YGAuthentication --version 1.2.5
                    
#r "nuget: YGAuthentication, 1.2.5"
                    
#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 YGAuthentication@1.2.5
                    
#: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=YGAuthentication&version=1.2.5
                    
Install as a Cake Addin
#tool nuget:?package=YGAuthentication&version=1.2.5
                    
Install as a Cake Tool

YGAuthentication

This package is used to authenticate and authorize with local Identity Server, where the user credentials are stored locally. Authentication process is done one time when the user enters their username and password on the login screen. Authorization process is done multiple times, anytime a user visit a page where the contents are available to authorized users only

Features:

  • A working Authentication / Authorization functionality for Blazor apps using JWT Claims. With WebAPI or Without.
  • Authentication with BCrypt one way encryption to api/loginEncr. Without encryption: api/login.
  • Authentication Direct without WebAPI with encryption: _authEncr.Authenticate.
  • Without encryption: _auth.Authenticate. where _authEncr and _auth are instances of AuthorizationJWTEncrypted and AuthorizationJWT, respectively. See program.cs for
  • Dependency Injection instatiation of these classes requires @inject in the consuming pages.

Host your own IDS. If you need help please hit me up. cs@yogigrantz.com or find me (Yogi Grantz) at codementor.com

Installation

Include this package in the project file

!!! Be sure to follow the working POC example. The source code includes POC Blazor Web app.

Usage

.csproj file:

<PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>disable</ImplicitUsings> </PropertyGroup>

<ItemGroup> <PackageReference Include="Blazored.LocalStorage" Version="4.5.0" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="YGAuthentication" Version="1.2.3" /> </ItemGroup>

AppSettings.json:

{
  "AuthName": "AnyNameOfYourChoice",
  "LoginPage":  "/Login"
}

program.cs:

using BlazorApp3.Components;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components;
using YGAuthentication.ActionFilters;
using YGAuthentication.Auth;
using YGAuthentication.States;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();

IConfiguration config = builder.Configuration;

string authName = config.GetValue<string>("AuthName");
string loginPage = config.GetValue<string>("LoginPage");
builder.Services.AddHttpClient(authName);
builder.Services.AddControllers();
builder.Services.AddBlazoredLocalStorage();

//sample one username and password. In reality you would populate the dictionary with username and pwd from IDS
string username = "yogi";
string pwd = "123";

int jwtExpMinutes = 60;

builder.Services.AddScoped<IAuthorizationJWT, AuthorizationJWT>(_ => {
    Dictionary<string, string> userCreds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    userCreds.Add(username, pwd); // Populate this with username and password from DB. The password is clear text

    return new AuthorizationJWT(userCreds, "username", jwtExpMinutes, "YourCompanyName", "General Audience");
    });

builder.Services.AddScoped<IAuthorizationJWTEncrypted, AuthorizationJWTEncrypted>(_ => {

    Dictionary<string, string> userCredsHashed = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    string pwdEncrypted = BCrypt.Net.BCrypt.HashPassword(pwd);
    userCredsHashed.Add(username, pwdEncrypted); // Populate this with username and password from DB. The password is BCrypt-hashed

    return new AuthorizationJWTEncrypted(userCredsHashed, "username", jwtExpMinutes, "YourCompanyName", "General Audience");
    });

builder.Services.AddScoped<IAppState, AppState>(_ => new AppState());
builder.Services.AddScoped<BasicAuthentication>();
builder.Services.AddScoped<IPageAuthCheck, PageAuthCheck>(ioc => new PageAuthCheck(ioc.GetService<NavigationManager>(), ioc.GetService<IHttpClientFactory>(), ioc.GetService<ILocalStorageService>(), authName, loginPage));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseRouting();

app.MapControllerRoute(
name: "mvc",
pattern: "{controller}/{action}/{id?}");

app.UseAntiforgery();
app.MapControllers();

app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();

app.Run();

Login Page - UI:

    @page "/Login"
    @rendermode InteractiveServer

    @using Blazored.LocalStorage
    @using YGAuthentication;
    @using YGAuthentication.States
    @using YGAuthentication.Auth

    @inject NavigationManager _navmgr
    @inject ILocalStorageService _localStorage
    @inject IHttpClientFactory _iHttpClientFactory
    @inject IAppState _appstate
    @inject IConfiguration _config
    @inject IAuthorizationJWT _auth
    @inject IAuthorizationJWTEncrypted _authEncr


    <div class="clearfix">
        <div class="floatLeft">Username</div>
        <div class="floatLeft"><input type="text" @bind="Username" /></div>
    </div>
    <div class="clearfix">
        <div class="floatLeft">Password</div>
        <div class="floatLeft"><input type="password" @bind="Password" /></div>
    </div>
    <br />
  <div class="clearfix">
    <button class="btn btn-primary" @onclick="ButtonLoginAuthDirect">Login Direct</button> |

    <button class="btn btn-primary" @onclick="ButtonLoginAuthDirectWithEncryption">Login Direct Encrypted</button> <br /><br />

    <button class="btn btn-dark" @onclick="ButtonLoginAuthWithWebAPI">Login With WebAPI</button> |

    <button class="btn btn-dark" @onclick="ButtonLoginAuthWithWebAPIEncr">Login With WebAPI Encrypted</button>
 </div>

    @if (Processing)
    {
        <div>
            <img src="ajax-loader.gif" />
        </div>
    }

    <div class="clearfix top20">
        @Message
    </div>

Login Page - MudBlazor / Code behind:

        using Newtonsoft.Json;
        using System.Text;
        using YGAuthentication;

        @using Blazored.LocalStorage
        @using YGAuthentication.States
        @using YGAuthentication.Auth

        @inject NavigationManager _navmgr
        @inject ILocalStorageService _localStorage
        @inject IHttpClientFactory _iHttpClientFactory
        @inject IAppState _appstate
        @inject IConfiguration _config
        @inject IAuthorizationJWT _auth
        @inject IAuthorizationJWTEncrypted _authEncr

        @page "/login"

        <br />
        <MudContainer MaxWidth="MaxWidth.Small">
            <MudCard>
                <MudCardContent>
                    <MudText Typo="Typo.h4" GutterBottom="true">Login</MudText>
                    <MudForm @ref="loginForm">
                        <MudTextField T="string" Label="Username" @bind-Value="Username" Required="true" FullWidth="true" Margin="Margin.Normal" />
                        <MudTextField T="string" Label="Password" @bind-Value="Password" Required="true" FullWidth="true" Margin="Margin.Normal" />

                        <MudButton Color="Color.Primary" Variant="Variant.Filled" FullWidth="true" OnClick="LoginAsync">Login</MudButton>
                    </MudForm>
                </MudCardContent>
            </MudCard>
        </MudContainer>

        @code {

            public MudForm loginForm { get; set; } = new MudForm();

        public partial class Login
        {
            public bool Processing { get; private set; }
            public string Username { get; set; } = "";
            public string Password { get; set; } = "";
            public string Message { get; set; } = "";

            protected override void OnInitialized()
            {
                base.OnInitialized();
                Message = "Please log in";
                this.StateHasChanged();
            }


            public async Task ButtonLoginAuthWithWebAPI()
            {
                IAuthentication authentication = new Authentication(_iHttpClientFactory, _config, _auth, _authEncr, _localStorage, _navmgr, _appstate);
                await authentication.LoginAuthWithWebAPIAsync(Username, Password);
            }

            public async Task ButtonLoginAuthWithWebAPIEncr()
            {
                IAuthentication authentication = new Authentication(_iHttpClientFactory, _config, _auth, _authEncr, _localStorage, _navmgr, _appstate);
                await authentication.LoginWithWebAPIEncrAsync(Username, Password);
            }

            public async Task ButtonLoginAuthDirect()
            {
                IAuthentication authentication = new Authentication(_iHttpClientFactory, _config, _auth, _authEncr, _localStorage, _navmgr, _appstate);
                await authentication.LoginDirectAsync(Username, Password);
            }

            public async Task ButtonLoginAuthDirectWithEncryption()
            {
                IAuthentication authentication = new Authentication(_iHttpClientFactory, _config, _auth, _authEncr, _localStorage, _navmgr, _appstate);
                await authentication.LoginDirectWithEncryptionAsync(Username, Password);
            }

Any razor pages that needs authorization:

@page "/"
@rendermode InteractiveServer
@using YGAuthentication.Auth
@using YGAuthentication.States
@inject IPageAuthCheck _pageAuthCheck
@inject IAppState _appState

@code {

    public string Message { get; private set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await _pageAuthCheck.AuthCheckAsync();
            string isIsNot = _pageAuthCheck.EditAuthorized ? "is" : "is not";
            Message = $"User {_pageAuthCheck.UserName} {isIsNot} authorized. Expiration: {_pageAuthCheck.AuthExp}";
            _appState.LoggedIn = true;
            StateHasChanged();
        }
    }
}
@using YGAuthentication.Auth
@using YGAuthentication.States
@inject IPageAuthCheck _pageAuthCheck
@inject IAppState _appState

@code {
    private bool _isLoggedIn;  // show restricted elements based on this flag

    protected override async Task OnInitializedAsync()
    {
        _appState.OnChange += Refresh;
    }

    private void Refresh()
    {
        _isLoggedIn = _appState.LoggedIn;
        this.StateHasChanged();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await _pageAuthCheck.AuthCheckAsync();
            string isIsNot = _pageAuthCheck.EditAuthorized ? "is" : "is not";
            _appState.LoggedIn = _pageAuthCheck.EditAuthorized;
            _isLoggedIn = _appState.LoggedIn;
            StateHasChanged();
        }
    }

    public void Dispose()
    {
        _appState.OnChange -= Refresh;
    }
}

Examples

A full working POC SampleAuthTest (MudBlazor) and BlazorApp3 (Plain Blazor) are provided in the Repo.

Output:

Blazor Web Application that uses log in

Dependencies

<ItemGroup> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="Blazored.LocalStorage" Version="4.5.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.0.2" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.2" /> </ItemGroup>

Contributing

Any new ideas on how to enhance this class without adding much complexity, please adhere to SOLID principle

License

This project is licensed under the MIT License(LICENSE).

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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.2.7 222 4/23/2025
1.2.6 147 4/11/2025
1.2.5 136 2/26/2025
1.2.4 137 2/25/2025
1.2.3 164 5/22/2024
1.2.2 158 5/20/2024
1.2.1 155 5/19/2024
1.2.0 147 5/19/2024
1.1.2 145 5/19/2024
1.1.1 164 5/19/2024
1.1.0 148 5/19/2024
1.0.0 160 5/18/2024

Added MudBlazor sample program to authenticate and authorize