YGAuthentication 1.0.0

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

// Install YGAuthentication as a Cake Tool
#tool nuget:?package=YGAuthentication&version=1.0.0                

YGAuthentication

A working Authentication / Authorization functionality using JWT Claims. This version 1.0 will authenticate clear passwords only. Look for the next version with overrides for authentication with BCrypt one way encryption

Installation

Include this package in the project file
Be sure to follow the working POC example

Usage

.csproj file:

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

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

AppSettings.json:

{
  "AuthName": "YGAuthentication",
  "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");

string username = "yogi";
string pwd = "123";

Dictionary<string, string> userCreds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
userCreds.Add(username, pwd); // Populate this with username and password from DB

int jwtExpMinutes = 60;
builder.Services.AddHttpClient(authName);
builder.Services.AddControllers();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddScoped<IAuthorizationJWT, AuthorizationJWT>(_ => new AuthorizationJWT(userCreds, "username", jwtExpMinutes, "Contoso", "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:

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 ButtonLogin()
    {

        try
        {
            Processing = true;
            this.Message = "Please wait ..";
            this.StateHasChanged();

            string authName = _config.GetValue<string>("AuthName");

            using (HttpClient httpclient = _iHttpClientFactory.CreateClient())
            {
                LoginDTO login = new LoginDTO() { Username = Username, Password = Password };

                string jsonPayload = JsonConvert.SerializeObject(login);

                HttpContent payload = new StringContent(jsonPayload, Encoding.UTF8, "application/json");

                var result = await httpclient.PostAsync($"{_navmgr.BaseUri}api/login", payload);
                if (result.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    string jwt = await result.Content.ReadAsStringAsync();
                    await _localStorage.SetItemAsStringAsync(authName, jwt);
                    _appstate.LoggedIn = true;

                    _navmgr.NavigateTo("/");
                }
                else
                {
                    this.Message = result.StatusCode.ToString();
                }

            }
        }
        catch (Exception ex)
        {
            this.Message = ex.Message;
        }

        Processing = false;
        this.StateHasChanged();
    }

}

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();
        }
    }
}
@code {
    private bool _isLoggedIn;  // show restricted elements based on this flag

    protected override void OnInitialized()
    {
        base.OnInitialized();
        _isLoggedIn = _appState.LoggedIn;
        _appState.OnChange += CheckUserLogin;
    }
    private void CheckUserLogin()
    {
        if (_isLoggedIn != _appState.LoggedIn)
        {
            _isLoggedIn = _appState.LoggedIn;
            this.StateHasChanged();
        }
    }

    private void SetLogout()
    {
        _isLoggedIn = false;
        this.StateHasChanged();
    }

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

Examples

A full working POC BlazorApp3 is provided in the Repo.

Output:

Blazor Web Application that uses log in

Dependencies

<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="7.5.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.5.2" />

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. 
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.3 104 5/22/2024
1.2.2 107 5/20/2024
1.2.1 102 5/19/2024
1.2.0 97 5/19/2024
1.1.2 96 5/19/2024
1.1.1 113 5/19/2024
1.1.0 95 5/19/2024
1.0.0 110 5/18/2024

Initial Release