Reworked dotbased auth config

This commit is contained in:
Max 2024-07-27 16:07:13 +02:00
parent 2b17ed4cd7
commit c092b8a679
6 changed files with 199 additions and 11 deletions

View File

@ -14,6 +14,13 @@ public class BasedAuthBuilder
configurationAction?.Invoke(Configuration);
services.AddSingleton<BasedAuthConfiguration>(Configuration);
if (Configuration.AuthDataProviderType == null)
throw new ArgumentNullException(nameof(Configuration.AuthDataProviderType), $"No '{nameof(IAuthDataProvider)}' configured!");
services.AddScoped(typeof(IAuthDataProvider), Configuration.AuthDataProviderType);
if (Configuration.SessionStateProviderType == null)
throw new ArgumentNullException(nameof(Configuration.SessionStateProviderType), $"No '{nameof(ISessionStateProvider)}' configured!");
services.AddScoped(typeof(ISessionStateProvider), Configuration.SessionStateProviderType);
services.AddScoped<AuthService>();
services.AddScoped<AuthenticationStateProvider, BasedServerAuthenticationStateProvider>();
@ -26,6 +33,4 @@ public class BasedAuthBuilder
}
public BasedAuthConfiguration Configuration { get; }
private readonly IServiceCollection _services;
public void AddSessionStateProvider<TSessionStateProviderType>() where TSessionStateProviderType : ISessionStateProvider => _services.AddScoped(typeof(ISessionStateProvider), typeof(TSessionStateProviderType));
}

View File

@ -1,5 +1,3 @@
using DotBased.ASP.Auth.Services;
namespace DotBased.ASP.Auth;
public class BasedAuthConfiguration
@ -9,12 +7,36 @@ public class BasedAuthConfiguration
/// </summary>
public bool AllowRegistration { get; set; }
//TODO: Callback when a user registers, so the application can handle sending emails or generate a code to complete the registration.
//TODO: Callback for validation email, phone number
/// <summary>
/// Allow no passwords on users, not recommended!
/// </summary>
public bool AllowEmptyPassword { get; set; } = false;
/// <summary>
/// This path is used for redirecting to the login page.
/// </summary>
public string LoginPath { get; set; } = string.Empty;
/// <summary>
/// The path that will be used if the logout is requested.
/// </summary>
public string LogoutPath { get; set; } = string.Empty;
/// <summary>
/// The max age before a AuthenticationState will expire (default: 7 days).
/// </summary>
public TimeSpan AuthenticationStateMaxAgeBeforeExpire { get; set; } = TimeSpan.FromDays(7);
//TODO: Data seeding
public Action<AuthService>? SeedData { get; set; }
/// <summary>
/// Can be used to seed a default user and/or group for first time use.
/// </summary>
public Action<IAuthDataProvider>? SeedData { get; set; }
public Type? AuthDataProviderType { get; private set; }
public void SetDataProviderType<TDataProviderType>() where TDataProviderType : IAuthDataProvider =>
AuthDataProviderType = typeof(TDataProviderType);
public Type? SessionStateProviderType { get; private set; }
public void SetSessionStateProviderType<TSessionStateProviderType>()
where TSessionStateProviderType : ISessionStateProvider =>
SessionStateProviderType = typeof(TSessionStateProviderType);
}

View File

@ -1,3 +1,7 @@
using DotBased.ASP.Auth.Scheme;
using DotBased.ASP.Auth.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
namespace DotBased.ASP.Auth;
@ -10,9 +14,47 @@ public static class DotBasedAuthDependencyInjection
/// <remarks>Use the app.UseAuthentication() and app.UseAuthorization()!</remarks>
/// <param name="services">Service collection</param>
/// <param name="configurationAction">DotBased auth configuration</param>
public static BasedAuthBuilder UseBasedServerAuth(this IServiceCollection services, Action<BasedAuthConfiguration>? configurationAction = null)
public static IServiceCollection AddBasedServerAuth(this IServiceCollection services, Action<BasedAuthConfiguration>? configurationAction = null)
{
var authBuilder = new BasedAuthBuilder(services, configurationAction);
return authBuilder;
/*var authBuilder = new BasedAuthBuilder(services, configurationAction);
return authBuilder;*/
var Configuration = new BasedAuthConfiguration();
configurationAction?.Invoke(Configuration);
services.AddSingleton<BasedAuthConfiguration>(Configuration);
if (Configuration.AuthDataProviderType == null)
throw new ArgumentNullException(nameof(Configuration.AuthDataProviderType), $"No '{nameof(IAuthDataProvider)}' configured!");
services.AddScoped(typeof(IAuthDataProvider), Configuration.AuthDataProviderType);
if (Configuration.SessionStateProviderType == null)
throw new ArgumentNullException(nameof(Configuration.SessionStateProviderType), $"No '{nameof(ISessionStateProvider)}' configured!");
services.AddScoped(typeof(ISessionStateProvider), Configuration.SessionStateProviderType);
services.AddScoped<AuthService>();
services.AddScoped<AuthenticationStateProvider, BasedServerAuthenticationStateProvider>();
services.AddAuthentication(options =>
{
options.DefaultScheme = BasedAuthenticationHandler.AuthenticationScheme;
}).AddScheme<BasedAuthenticationHandlerOptions, BasedAuthenticationHandler>(BasedAuthenticationHandler.AuthenticationScheme, null);
services.AddAuthorization();
services.AddCascadingAuthenticationState();
return services;
}
public static WebApplication UseBasedServerAuth(this WebApplication app)
{
app.UseAuthentication();
app.UseAuthorization();
// Data
var authConfig = app.Services.GetService<BasedAuthConfiguration>();
if (authConfig == null)
throw new NullReferenceException($"{nameof(BasedAuthConfiguration)} is null!");
if (authConfig.AuthDataProviderType == null)
throw new NullReferenceException($"{nameof(authConfig.AuthDataProviderType)} is null, cannot instantiate an instance of {nameof(IAuthDataProvider)}");
var dataProvider = (IAuthDataProvider?)Activator.CreateInstance(authConfig.AuthDataProviderType);
if (dataProvider != null) authConfig.SeedData?.Invoke(dataProvider);
return app;
}
}

View File

@ -1,6 +1,5 @@
using DotBased.ASP.Auth.Domains.Auth;
using DotBased.ASP.Auth.Domains.Identity;
using DotBased.Objects;
namespace DotBased.ASP.Auth;

View File

@ -0,0 +1,83 @@
using DotBased.ASP.Auth.Domains.Auth;
using DotBased.ASP.Auth.Domains.Identity;
namespace DotBased.ASP.Auth;
/// <summary>
/// In memory data provider, for testing only!
/// </summary>
public class MemoryAuthDataProvider : IAuthDataProvider
{
private Dictionary<string, UserModel> _userDict = [];
private Dictionary<string, GroupModel> _groupDict = [];
private Dictionary<string, AuthenticationStateModel> _authenticationDict = [];
public async Task<Result> CreateUserAsync(UserModel user)
{
throw new NotImplementedException();
}
public async Task<Result> UpdateUserAsync(UserModel user)
{
throw new NotImplementedException();
}
public async Task<Result> DeleteUserAsync(UserModel user)
{
throw new NotImplementedException();
}
public async Task<Result<UserModel>> GetUserAsync(string id, string email, string username)
{
throw new NotImplementedException();
}
public async Task<ListResult<UserItemModel>> GetUsersAsync(int start = 0, int amount = 30, string search = "")
{
throw new NotImplementedException();
}
public async Task<Result> CreateGroupAsync(GroupModel group)
{
throw new NotImplementedException();
}
public async Task<Result> UpdateGroupAsync(GroupModel group)
{
throw new NotImplementedException();
}
public async Task<Result> DeleteGroupAsync(GroupModel group)
{
throw new NotImplementedException();
}
public async Task<Result<GroupModel>> GetGroupAsync(string id)
{
throw new NotImplementedException();
}
public async Task<ListResult<GroupItemModel>> GetGroupsAsync(int start = 0, int amount = 30, string search = "")
{
throw new NotImplementedException();
}
public async Task<Result> CreateAuthenticationStateAsync(AuthenticationStateModel authenticationState)
{
throw new NotImplementedException();
}
public async Task<Result> UpdateAuthenticationStateAsync(AuthenticationStateModel authenticationState)
{
throw new NotImplementedException();
}
public async Task<Result> DeleteAuthenticationStateAsync(AuthenticationStateModel authenticationState)
{
throw new NotImplementedException();
}
public async Task<Result<AuthenticationStateModel>> GetAuthenticationStateAsync(string id)
{
throw new NotImplementedException();
}
}

View File

@ -1,6 +1,43 @@
using DotBased.ASP.Auth.Domains;
using DotBased.ASP.Auth.Domains.Auth;
using DotBased.Extensions;
using DotBased.Logging;
namespace DotBased.ASP.Auth.Services;
public class AuthService
{
public AuthService(IAuthDataProvider dataProvider)
{
_dataProvider = dataProvider;
_logger = LogService.RegisterLogger(typeof(AuthService));
}
private readonly IAuthDataProvider _dataProvider;
private readonly ILogger _logger;
public async Task<Result<AuthenticationStateModel>> LoginAsync(LoginModel login)
{
if (login.UserName.IsNullOrWhiteSpace())
return Result<AuthenticationStateModel>.Failed("Username argument is empty!");
var userResult = await _dataProvider.GetUserAsync(string.Empty, login.Email, login.UserName);
//TODO: validate user password and create a session state
return Result<AuthenticationStateModel>.Failed("");
}
public async Task<Result> Logout(string state)
{
if (state.IsNullOrWhiteSpace())
return Result.Failed($"Argument {nameof(state)} is empty!");
var stateResult = await _dataProvider.GetAuthenticationStateAsync(state);
if (!stateResult.Success || stateResult.Value == null)
return stateResult;
var authState = stateResult.Value;
//TODO: Update state to logged out and update the state
var updatedStateResult = await _dataProvider.UpdateAuthenticationStateAsync(authState);
if (updatedStateResult.Success) return updatedStateResult;
_logger.Warning(updatedStateResult.Message);
return updatedStateResult;
}
}