Codelabs.SimpleAuthentication
2.0.7
dotnet add package Codelabs.SimpleAuthentication --version 2.0.7
NuGet\Install-Package Codelabs.SimpleAuthentication -Version 2.0.7
<PackageReference Include="Codelabs.SimpleAuthentication" Version="2.0.7" />
paket add Codelabs.SimpleAuthentication --version 2.0.7
#r "nuget: Codelabs.SimpleAuthentication, 2.0.7"
// Install Codelabs.SimpleAuthentication as a Cake Addin #addin nuget:?package=Codelabs.SimpleAuthentication&version=2.0.7 // Install Codelabs.SimpleAuthentication as a Cake Tool #tool nuget:?package=Codelabs.SimpleAuthentication&version=2.0.7
SimpleAuthentication
Currently Supports Blazor Server & ASP.NET Core MVC, Web API JWT. Next Release will include a clientside package to support WASM/MAUI - Blazor Hybrid.
How to set up
Download Nuget Package
Install-Package Codelabs.SimpleAuthentication
For Blazor Server Apps You Need A Custom AuthenticationState Provider. Just Copy the class below and add it in your project.
internal class ServerSideAuthenticationStateProvider
: RevalidatingServerAuthenticationStateProvider
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly IdentityOptions _options;
public ServerSideAuthenticationStateProvider(
ILoggerFactory loggerFactory,
IServiceScopeFactory scopeFactory,
IOptions<IdentityOptions> optionsAccessor)
: base(loggerFactory)
{
_scopeFactory = scopeFactory;
_options = optionsAccessor.Value;
}
protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30);
protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
// Get the user manager from a new scope to ensure it fetches fresh data
var scope = _scopeFactory.CreateScope();
try
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
return await ValidateSecurityStampAsync(userManager, authenticationState.User);
}
finally
{
if (scope is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
scope.Dispose();
}
}
}
private async Task<bool> ValidateSecurityStampAsync(UserManager<User> userManager, ClaimsPrincipal principal)
{
var user = await userManager.GetUserAsync(principal);
if (user == null)
{
return false;
}
else if (!userManager.SupportsUserSecurityStamp)
{
return true;
}
else
{
var principalStamp = principal.FindFirstValue(_options.ClaimsIdentity.SecurityStampClaimType);
var userStamp = await userManager.GetSecurityStampAsync(user);
return principalStamp == userStamp;
}
}
}
}
Next Install <code>Microsoft.EntityFrameworkCore.Tools</code> and the Database provider of your choice in the demo <code>Microsoft.EntityFrameworkCore.SQLite</code> is used. <br/> Lastly in your <code>Program.cs</code> configure SimpleAuthentication Like below.
using Demo.Data;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore;
using SimpleAuthentication;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSimpleAuthentication(userStoreOptions =>
{
userStoreOptions.UseSqlite("Data Source = Identity.db");
});
builder.Services.AddScoped<AuthenticationStateProvider, ServerSideAuthenticationStateProvider>();
builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// 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.UseAuthentication();
app.UseAuthorization();
app.UseSimpleAuthentication();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
To Futher Customize Identity You can provide identityOptions paramter as shown below:
builder.Services.AddSimpleAuthentication(dbOptions =>
{
dbOptions.UseSqlite("Data Source = demo.db");
}, identityOptions => {
identityOptions.SignIn.RequireConfirmedEmail = true;
identityOptions.Password.RequiredLength = 8;
identityOptions.Password.RequireDigit = false;
identityOptions.Password.RequireUppercase = false;
identityOptions.Password.RequireLowercase = false;
});
The code below shows a demo login page using Razor.
@page "/login"
@attribute [AllowAnonymous]
@layout LoginLayout
<div class="container center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h4 class="text-primary">Login</h4>
</div>
<div class="card-body">
<EditForm Model="@Model" OnValidSubmit="LoginAsync" id="loginForm">
<DataAnnotationsValidator />
<div class="form-group mb-4">
<label for="username">Username:</label>
<InputText @bind-Value="Model.UserName" type="text" class="form-control" id="username" name="username" />
<ValidationMessage For="()=>Model.UserName" />
</div>
<div class="form-group mb-4">
<label for="password">Password:</label>
<InputText @bind-Value="Model.Password" type="password" class="form-control" id="password" name="password" />
<ValidationMessage For="()=>Model.Password" />
</div>
<button title="Login now." type="submit" class="btn btn-primary">Login</button>
<a href="register" title="Don't have an account yet? Register."
class="btn btn-success mx-4">Register</a>
</EditForm>
@if (failed)
{
<div id="e-message" class="mt-4 pa-2">
<div class="alert alert-danger">
<div class="d-flex align-items-center">
<div>
<i id="e-icon" class="oi oi-warning"></i>
</div>
<div class="ml-4 mt-3">
<p id="e-message-content">@errorMessage</p>
</div>
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
@inject NavigationManager navManager;
@code {
LoginRequest Model = new();
string errorMessage = string.Empty;
bool failed;
async Task LoginAsync()
{
Model.ReturnUrl="/fetchdata";
var authResult = await authenticationService.LoginAsync(Model);
if(authResult.Succeeded)
{
navManager.NavigateTo($"/login?key={authResult.Key}",true);
}else
{
failed = true;
errorMessage = authResult.Message;
StateHasChanged();
}
}
}
Web APIs JWT
The following Example Shows Easy Set Up From the WebAPI Demo Project <code>Program.cs</code> for Use In ASP.NET Core Web APIs.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SimpleAuthentication;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
//Package Already Ships with Swagger.
builder.UseSimpleAuthenticationJwt(options =>
{
options.UseSqlite("Data Source = demo.db");
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast",[Authorize] () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateTime.Now.AddDays(index),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
//JWT Token EndPoint
app.MapPost("/token", async (ITokenService tokenService, [FromBody] TokenRequest request) =>
{
return await tokenService.GetAccessToken(request);
});
app.Run();
internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
If you are going to use JWT for your WebApis, You will need a random string of at least 256 bytes to be used as a secret key in JWT Generation and Decoding. This key is to be placed in appsettings.json as shown below.
{
"SimpleJwtConfig": {
"Secret": "SDKSDHKSDHSIKSIFN2328386sdisadq55654esdw"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
That's it. For more info see Demos on GitHub
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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. |
-
net6.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 6.0.16)
- Microsoft.AspNetCore.Authorization (>= 6.0.14)
- Microsoft.AspNetCore.Identity (>= 2.2.0)
- Microsoft.AspNetCore.Identity.EntityFrameworkCore (>= 6.0.12)
- Microsoft.Extensions.Identity.Core (>= 6.0.12)
- Microsoft.IdentityModel.Tokens (>= 6.30.1)
- Newtonsoft.Json (>= 13.0.2)
- Swashbuckle.AspNetCore (>= 6.2.3)
- System.IdentityModel.Tokens.Jwt (>= 6.30.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.