mirror of
https://github.com/hmaxnl/DotBased.git
synced 2025-01-18 18:14:20 +01:00
Working base login
This commit is contained in:
parent
d98634d888
commit
8531079a16
|
@ -14,41 +14,26 @@ public class AuthDataCache
|
||||||
|
|
||||||
private readonly CacheNodeCollection<AuthenticationStateModel> _authenticationStateCollection = [];
|
private readonly CacheNodeCollection<AuthenticationStateModel> _authenticationStateCollection = [];
|
||||||
|
|
||||||
public Result PurgeSessionFromCache(string id) => _authenticationStateCollection.Remove(id) ? Result.Ok() : Result.Failed("Failed to purge session state from cache! Or the session was not cached...");
|
public Result PurgeSessionState(string id) => _authenticationStateCollection.Remove(id) ? Result.Ok() : Result.Failed("Failed to purge session state from cache! Or the session was not cached...");
|
||||||
|
|
||||||
public async Task<Result<AuthenticationStateModel>> RequestAuthStateAsync(IAuthDataRepository dataRepository, string id)
|
public void CacheSessionState(AuthenticationStateModel state) => _authenticationStateCollection.Insert(new CacheNode<AuthenticationStateModel>(state));
|
||||||
|
|
||||||
|
public Result<AuthenticationStateModel> RequestSessionState(string id)
|
||||||
{
|
{
|
||||||
if (_authenticationStateCollection.TryGetValue(id, out var node))
|
if (!_authenticationStateCollection.TryGetValue(id, out var node))
|
||||||
|
return Result<AuthenticationStateModel>.Failed("No cached object found!");
|
||||||
|
string failedMsg;
|
||||||
|
if (node.Object != null)
|
||||||
{
|
{
|
||||||
if (node.Object == null)
|
|
||||||
{
|
|
||||||
_authenticationStateCollection.Remove(id);
|
|
||||||
return Result<AuthenticationStateModel>.Failed($"Returned object is null, removing entry [{id}] from cache!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.IsValidLifespan(_configuration.CachedAuthSessionLifespan))
|
if (node.IsValidLifespan(_configuration.CachedAuthSessionLifespan))
|
||||||
return Result<AuthenticationStateModel>.Ok(node.Object);
|
return Result<AuthenticationStateModel>.Ok(node.Object);
|
||||||
|
failedMsg = $"Session has invalid lifespan, removing entry: [{id}] from cache!";
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbResult = await dataRepository.GetAuthenticationStateAsync(id);
|
|
||||||
if (!dbResult.Success || dbResult.Value == null)
|
|
||||||
{
|
|
||||||
_authenticationStateCollection.Remove(id);
|
|
||||||
return Result<AuthenticationStateModel>.Failed("Unknown session state!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
node = new CacheNode<AuthenticationStateModel>(dbResult.Value);
|
|
||||||
else
|
else
|
||||||
node.UpdateObject(dbResult.Value);
|
failedMsg = $"Returned object is null, removing entry: [{id}] from cache!";
|
||||||
if (node.Object != null)
|
_authenticationStateCollection.Remove(id);
|
||||||
return Result<AuthenticationStateModel>.Ok(node.Object);
|
return Result<AuthenticationStateModel>.Failed(failedMsg);
|
||||||
return node.Object != null ? Result<AuthenticationStateModel>.Ok(node.Object) : Result<AuthenticationStateModel>.Failed("Failed to get db object!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CacheNode<T> where T : class
|
public class CacheNode<T> where T : class
|
||||||
|
@ -86,4 +71,24 @@ public class CacheNode<T> where T : class
|
||||||
public class CacheNodeCollection<TItem> : KeyedCollection<string, CacheNode<TItem>> where TItem : class
|
public class CacheNodeCollection<TItem> : KeyedCollection<string, CacheNode<TItem>> where TItem : class
|
||||||
{
|
{
|
||||||
protected override string GetKeyForItem(CacheNode<TItem> item) => item.Object?.ToString() ?? string.Empty;
|
protected override string GetKeyForItem(CacheNode<TItem> item) => item.Object?.ToString() ?? string.Empty;
|
||||||
|
|
||||||
|
public new CacheNode<TItem>? this[string id]
|
||||||
|
{
|
||||||
|
get => TryGetValue(id, out CacheNode<TItem>? nodeValue) ? nodeValue : null;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
if (TryGetValue(id, out CacheNode<TItem>? nodeValue))
|
||||||
|
Remove(nodeValue);
|
||||||
|
Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(CacheNode<TItem> node)
|
||||||
|
{
|
||||||
|
if (Contains(node))
|
||||||
|
Remove(node);
|
||||||
|
Add(node);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,10 @@ public class BasedAuthConfiguration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LogoutPath { get; set; } = string.Empty;
|
public string LogoutPath { get; set; } = string.Empty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// The page that the client will be redirected to after logging out.
|
||||||
|
/// </summary>
|
||||||
|
public string LoggedOutPath { get; set; } = string.Empty;
|
||||||
|
/// <summary>
|
||||||
/// The max age before a AuthenticationState will expire (default: 7 days).
|
/// The max age before a AuthenticationState will expire (default: 7 days).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan AuthenticationStateMaxAgeBeforeExpire { get; set; } = TimeSpan.FromDays(7);
|
public TimeSpan AuthenticationStateMaxAgeBeforeExpire { get; set; } = TimeSpan.FromDays(7);
|
||||||
|
|
6
DotBased.ASP.Auth/BasedAuthDefaults.cs
Normal file
6
DotBased.ASP.Auth/BasedAuthDefaults.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.ASP.Auth;
|
||||||
|
|
||||||
|
public static class BasedAuthDefaults
|
||||||
|
{
|
||||||
|
public const string AuthenticationScheme = "DotBasedAuthentication";
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using DotBased.ASP.Auth.Scheme;
|
using DotBased.ASP.Auth.Services;
|
||||||
using DotBased.Logging;
|
using DotBased.Logging;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using Microsoft.AspNetCore.Components.Server;
|
using Microsoft.AspNetCore.Components.Server;
|
||||||
|
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using ILogger = DotBased.Logging.ILogger;
|
using ILogger = DotBased.Logging.ILogger;
|
||||||
|
|
||||||
namespace DotBased.ASP.Auth;
|
namespace DotBased.ASP.Auth;
|
||||||
|
@ -12,22 +15,30 @@ namespace DotBased.ASP.Auth;
|
||||||
// Handles roles
|
// Handles roles
|
||||||
public class BasedServerAuthenticationStateProvider : ServerAuthenticationStateProvider
|
public class BasedServerAuthenticationStateProvider : ServerAuthenticationStateProvider
|
||||||
{
|
{
|
||||||
public BasedServerAuthenticationStateProvider(BasedAuthConfiguration configuration, ISessionStateProvider stateProvider)
|
public BasedServerAuthenticationStateProvider(BasedAuthConfiguration configuration, ProtectedLocalStorage localStorage, SecurityService securityService)
|
||||||
{
|
{
|
||||||
_config = configuration;
|
_config = configuration;
|
||||||
_stateProvider = stateProvider;
|
//_stateProvider = stateProvider;
|
||||||
|
_localStorage = localStorage;
|
||||||
|
_securityService = securityService;
|
||||||
_logger = LogService.RegisterLogger(typeof(BasedServerAuthenticationStateProvider));
|
_logger = LogService.RegisterLogger(typeof(BasedServerAuthenticationStateProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
private BasedAuthConfiguration _config;
|
private BasedAuthConfiguration _config;
|
||||||
private ISessionStateProvider _stateProvider;
|
private ISessionStateProvider _stateProvider;
|
||||||
|
private ProtectedLocalStorage _localStorage;
|
||||||
|
private SecurityService _securityService;
|
||||||
private ILogger _logger;
|
private ILogger _logger;
|
||||||
private readonly AuthenticationState _loggedInState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>() { new Claim(ClaimTypes.Role, "Admin"), new Claim(ClaimTypes.Name, "Anon") }, BasedAuthenticationHandler.AuthenticationScheme)));
|
private readonly AuthenticationState _loggedInState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>() { new Claim(ClaimTypes.Role, "Admin"),new Claim(ClaimTypes.Role, "nottest"), new Claim(ClaimTypes.Name, "Anon") }, BasedAuthDefaults.AuthenticationScheme)));
|
||||||
private readonly AuthenticationState _anonState = new AuthenticationState(new ClaimsPrincipal());
|
private readonly AuthenticationState _anonState = new AuthenticationState(new ClaimsPrincipal());
|
||||||
|
|
||||||
|
|
||||||
public override Task<AuthenticationState> GetAuthenticationStateAsync()
|
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||||
{
|
{
|
||||||
return Task.FromResult(_loggedInState);
|
var sessionIdResult = await _localStorage.GetAsync<string>("dotbased_session");
|
||||||
|
if (!sessionIdResult.Success || sessionIdResult.Value == null)
|
||||||
|
return _anonState;
|
||||||
|
var stateResult = await _securityService.GetAuthenticationFromSession(sessionIdResult.Value);
|
||||||
|
return stateResult is { Success: true, Value: not null } ? stateResult.Value : _anonState;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
using DotBased.ASP.Auth.Scheme;
|
|
||||||
using DotBased.ASP.Auth.Services;
|
using DotBased.ASP.Auth.Services;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
@ -23,18 +22,18 @@ public static class DotBasedAuthDependencyInjection
|
||||||
if (Configuration.AuthDataRepositoryType == null)
|
if (Configuration.AuthDataRepositoryType == null)
|
||||||
throw new ArgumentNullException(nameof(Configuration.AuthDataRepositoryType), $"No '{nameof(IAuthDataRepository)}' configured!");
|
throw new ArgumentNullException(nameof(Configuration.AuthDataRepositoryType), $"No '{nameof(IAuthDataRepository)}' configured!");
|
||||||
services.AddScoped(typeof(IAuthDataRepository), Configuration.AuthDataRepositoryType);
|
services.AddScoped(typeof(IAuthDataRepository), Configuration.AuthDataRepositoryType);
|
||||||
if (Configuration.SessionStateProviderType == null)
|
/*if (Configuration.SessionStateProviderType == null)
|
||||||
throw new ArgumentNullException(nameof(Configuration.SessionStateProviderType), $"No '{nameof(ISessionStateProvider)}' configured!");
|
throw new ArgumentNullException(nameof(Configuration.SessionStateProviderType), $"No '{nameof(ISessionStateProvider)}' configured!");
|
||||||
services.AddScoped(typeof(ISessionStateProvider), Configuration.SessionStateProviderType);
|
services.AddScoped(typeof(ISessionStateProvider), Configuration.SessionStateProviderType);*/
|
||||||
|
|
||||||
services.AddSingleton<AuthDataCache>();
|
services.AddSingleton<AuthDataCache>();
|
||||||
services.AddScoped<AuthService>();
|
services.AddScoped<SecurityService>();
|
||||||
|
|
||||||
services.AddScoped<AuthenticationStateProvider, BasedServerAuthenticationStateProvider>();
|
services.AddScoped<AuthenticationStateProvider, BasedServerAuthenticationStateProvider>();
|
||||||
services.AddAuthentication(options =>
|
services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultScheme = BasedAuthenticationHandler.AuthenticationScheme;
|
options.DefaultScheme = BasedAuthDefaults.AuthenticationScheme;
|
||||||
}).AddScheme<BasedAuthenticationHandlerOptions, BasedAuthenticationHandler>(BasedAuthenticationHandler.AuthenticationScheme, null);
|
});/*.AddScheme<BasedAuthenticationHandlerOptions, BasedAuthenticationHandler>(BasedAuthDefaults.AuthenticationScheme, null);*/
|
||||||
services.AddAuthorization();
|
services.AddAuthorization();
|
||||||
services.AddCascadingAuthenticationState();
|
services.AddCascadingAuthenticationState();
|
||||||
return services;
|
return services;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using DotBased.ASP.Auth.Domains.Auth;
|
using DotBased.ASP.Auth.Domains.Auth;
|
||||||
using DotBased.ASP.Auth.Domains.Identity;
|
using DotBased.ASP.Auth.Domains.Identity;
|
||||||
|
using DotBased.Extensions;
|
||||||
|
|
||||||
namespace DotBased.ASP.Auth;
|
namespace DotBased.ASP.Auth;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -9,21 +10,17 @@ namespace DotBased.ASP.Auth;
|
||||||
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")]
|
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")]
|
||||||
public class MemoryAuthDataRepository : IAuthDataRepository
|
public class MemoryAuthDataRepository : IAuthDataRepository
|
||||||
{
|
{
|
||||||
private readonly List<UserModel> _userList = [];
|
|
||||||
private readonly List<GroupModel> _groupList = [];
|
|
||||||
private readonly List<AuthenticationStateModel> _authenticationStateList = [];
|
|
||||||
|
|
||||||
public async Task<Result> CreateUserAsync(UserModel user)
|
public async Task<Result> CreateUserAsync(UserModel user)
|
||||||
{
|
{
|
||||||
if (_userList.Any(x => x.Id == user.Id || x.Email == user.Email))
|
if (MemoryData.users.Any(x => x.Id == user.Id || x.Email == user.Email))
|
||||||
return Result.Failed("User already exists.");
|
return Result.Failed("User already exists.");
|
||||||
_userList.Add(user);
|
MemoryData.users.Add(user);
|
||||||
return Result.Ok();
|
return Result.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> UpdateUserAsync(UserModel user)
|
public async Task<Result> UpdateUserAsync(UserModel user)
|
||||||
{
|
{
|
||||||
if (_userList.All(x => x.Id != user.Id))
|
if (MemoryData.users.All(x => x.Id != user.Id))
|
||||||
return Result.Failed("User does not exist!");
|
return Result.Failed("User does not exist!");
|
||||||
|
|
||||||
return Result.Ok();
|
return Result.Ok();
|
||||||
|
@ -34,9 +31,16 @@ public class MemoryAuthDataRepository : IAuthDataRepository
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Result<UserModel>> GetUserAsync(string id, string email, string username)
|
public async Task<Result<UserModel>> GetUserAsync(string id, string email, string username)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
UserModel? userModel = null;
|
||||||
|
if (!id.IsNullOrWhiteSpace())
|
||||||
|
userModel = MemoryData.users.FirstOrDefault(u => u.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!email.IsNullOrWhiteSpace())
|
||||||
|
userModel = MemoryData.users.FirstOrDefault(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!username.IsNullOrWhiteSpace())
|
||||||
|
userModel = MemoryData.users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase));
|
||||||
|
return userModel != null ? Result<UserModel>.Ok(userModel) : Result<UserModel>.Failed("No user found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ListResult<UserItemModel>> GetUsersAsync(int start = 0, int amount = 30, string search = "")
|
public Task<ListResult<UserItemModel>> GetUsersAsync(int start = 0, int amount = 30, string search = "")
|
||||||
|
@ -69,9 +73,11 @@ public class MemoryAuthDataRepository : IAuthDataRepository
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Result> CreateAuthenticationStateAsync(AuthenticationStateModel authenticationState)
|
public async Task<Result> CreateAuthenticationStateAsync(AuthenticationStateModel authenticationState)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (MemoryData.AuthenticationStates.Contains(authenticationState)) return Result.Failed("Item already exists!");
|
||||||
|
MemoryData.AuthenticationStates.Add(authenticationState);
|
||||||
|
return Result.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Result> UpdateAuthenticationStateAsync(AuthenticationStateModel authenticationState)
|
public Task<Result> UpdateAuthenticationStateAsync(AuthenticationStateModel authenticationState)
|
||||||
|
@ -79,13 +85,23 @@ public class MemoryAuthDataRepository : IAuthDataRepository
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Result> DeleteAuthenticationStateAsync(AuthenticationStateModel authenticationState)
|
public async Task<Result> DeleteAuthenticationStateAsync(AuthenticationStateModel authenticationState)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
MemoryData.AuthenticationStates.Remove(authenticationState);
|
||||||
|
return Result.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Result<AuthenticationStateModel>> GetAuthenticationStateAsync(string id)
|
public async Task<Result<AuthenticationStateModel>> GetAuthenticationStateAsync(string id)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var item = MemoryData.AuthenticationStates.FirstOrDefault(x => x.Id == id);
|
||||||
|
if (item == null) return Result<AuthenticationStateModel>.Failed("Could not get the session state!");
|
||||||
|
return Result<AuthenticationStateModel>.Ok(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static class MemoryData
|
||||||
|
{
|
||||||
|
public static readonly List<UserModel> users = [];
|
||||||
|
public static readonly List<GroupModel> Groups = [];
|
||||||
|
public static readonly List<AuthenticationStateModel> AuthenticationStates = [];
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace DotBased.ASP.Auth.Scheme;
|
|
||||||
|
|
||||||
// Handles if a user is logged in
|
|
||||||
public class BasedAuthenticationHandler : AuthenticationHandler<BasedAuthenticationHandlerOptions>
|
|
||||||
{
|
|
||||||
public const string AuthenticationScheme = "DotBasedAuthentication";
|
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
public BasedAuthenticationHandler(IOptionsMonitor<BasedAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasedAuthenticationHandler(IOptionsMonitor<BasedAuthenticationHandlerOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
|
||||||
{
|
|
||||||
/*var principal = new ClaimsPrincipal(new ClaimsIdentity());*/
|
|
||||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>() { new Claim(ClaimTypes.Role, "Admin"), new Claim(ClaimTypes.Name, "Anon") }, AuthenticationScheme));
|
|
||||||
var ticket = new AuthenticationTicket(principal, AuthenticationScheme);
|
|
||||||
return Task.FromResult(AuthenticateResult.Success(ticket));
|
|
||||||
/*return AuthenticateResult.Fail("No login found!");*/
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
|
|
||||||
namespace DotBased.ASP.Auth.Scheme;
|
|
||||||
|
|
||||||
public class BasedAuthenticationHandlerOptions : AuthenticationSchemeOptions
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
using DotBased.ASP.Auth.Domains;
|
|
||||||
using DotBased.ASP.Auth.Domains.Auth;
|
|
||||||
using DotBased.ASP.Auth.Domains.Identity;
|
|
||||||
using DotBased.Extensions;
|
|
||||||
using DotBased.Logging;
|
|
||||||
|
|
||||||
namespace DotBased.ASP.Auth.Services;
|
|
||||||
|
|
||||||
public class AuthService
|
|
||||||
{
|
|
||||||
public AuthService(IAuthDataRepository authDataRepository, AuthDataCache dataCache)
|
|
||||||
{
|
|
||||||
_authDataRepository = authDataRepository;
|
|
||||||
_dataCache = dataCache;
|
|
||||||
_logger = LogService.RegisterLogger(typeof(AuthService));
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly IAuthDataRepository _authDataRepository;
|
|
||||||
private readonly AuthDataCache _dataCache;
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
public async Task<Result<AuthenticationStateModel>> LoginAsync(LoginModel login)
|
|
||||||
{
|
|
||||||
UserModel? user = null;
|
|
||||||
Result<UserModel> usrResult;
|
|
||||||
if (!login.UserName.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
usrResult = await _authDataRepository.GetUserAsync(string.Empty, string.Empty, login.UserName);
|
|
||||||
if (usrResult is { Success: true, Value: not null })
|
|
||||||
user = usrResult.Value;
|
|
||||||
}
|
|
||||||
else if (!login.Email.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
usrResult = await _authDataRepository.GetUserAsync(string.Empty, login.Email, string.Empty);
|
|
||||||
if (usrResult is { Success: true, Value: not null })
|
|
||||||
user = usrResult.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return Result<AuthenticationStateModel>.Failed("Username & Email is empty, cannot login!");
|
|
||||||
|
|
||||||
if (user == null || !usrResult.Success)
|
|
||||||
return new Result<AuthenticationStateModel>(usrResult);
|
|
||||||
|
|
||||||
if (user.PasswordHash != login.Password) //TODO: Hash password and compare
|
|
||||||
return Result<AuthenticationStateModel>.Failed("Login failed, invalid password.");
|
|
||||||
var state = new AuthenticationStateModel(user);
|
|
||||||
await _authDataRepository.CreateAuthenticationStateAsync(state);
|
|
||||||
return Result<AuthenticationStateModel>.Ok(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;*/
|
|
||||||
return Result.Failed($"Argument {nameof(state)} is empty!"); // <- TEMP
|
|
||||||
}
|
|
||||||
}
|
|
122
DotBased.ASP.Auth/Services/SecurityService.cs
Normal file
122
DotBased.ASP.Auth/Services/SecurityService.cs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using DotBased.ASP.Auth.Domains;
|
||||||
|
using DotBased.ASP.Auth.Domains.Auth;
|
||||||
|
using DotBased.ASP.Auth.Domains.Identity;
|
||||||
|
using DotBased.Extensions;
|
||||||
|
using DotBased.Logging;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
|
||||||
|
namespace DotBased.ASP.Auth.Services;
|
||||||
|
|
||||||
|
public class SecurityService
|
||||||
|
{
|
||||||
|
public SecurityService(IAuthDataRepository authDataRepository, AuthDataCache dataCache)
|
||||||
|
{
|
||||||
|
_authDataRepository = authDataRepository;
|
||||||
|
_dataCache = dataCache;
|
||||||
|
_logger = LogService.RegisterLogger(typeof(SecurityService));
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IAuthDataRepository _authDataRepository;
|
||||||
|
private readonly AuthDataCache _dataCache;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public async Task<Result<AuthenticationState>> GetAuthenticationFromSession(string id)
|
||||||
|
{
|
||||||
|
if (id.IsNullOrWhiteSpace())
|
||||||
|
return Result<AuthenticationState>.Failed("No valid id!");
|
||||||
|
AuthenticationStateModel? authState = null;
|
||||||
|
var stateCache = _dataCache.RequestSessionState(id);
|
||||||
|
if (!stateCache.Success || stateCache.Value == null)
|
||||||
|
{
|
||||||
|
var stateResult = await _authDataRepository.GetAuthenticationStateAsync(id);
|
||||||
|
if (stateResult is { Success: true, Value: not null })
|
||||||
|
authState = stateResult.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
authState = stateCache.Value;
|
||||||
|
|
||||||
|
if (authState == null)
|
||||||
|
return Result<AuthenticationState>.Failed("Failed to get state!");
|
||||||
|
|
||||||
|
var userResult = await _authDataRepository.GetUserAsync(authState.UserId, string.Empty, string.Empty);
|
||||||
|
if (userResult is not { Success: true, Value: not null })
|
||||||
|
return Result<AuthenticationState>.Failed("Failed to get user from state!");
|
||||||
|
var claims = new List<Claim>()
|
||||||
|
{
|
||||||
|
new(ClaimTypes.Name, userResult.Value.Name),
|
||||||
|
new(ClaimTypes.NameIdentifier, userResult.Value.UserName),
|
||||||
|
new(ClaimTypes.Surname, userResult.Value.FamilyName),
|
||||||
|
new(ClaimTypes.Email, userResult.Value.Email)
|
||||||
|
};
|
||||||
|
claims.AddRange(userResult.Value.Roles.Select(role => new Claim(ClaimTypes.Role, role.Name)).ToList());
|
||||||
|
var claimsIdentity = new ClaimsIdentity(claims, BasedAuthDefaults.AuthenticationScheme);
|
||||||
|
var auth = new AuthenticationState(new ClaimsPrincipal(claimsIdentity));
|
||||||
|
return Result<AuthenticationState>.Ok(auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<AuthenticationStateModel>> LoginAsync(LoginModel login)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UserModel? user = null;
|
||||||
|
Result<UserModel> usrResult;
|
||||||
|
if (!login.UserName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
usrResult = await _authDataRepository.GetUserAsync(string.Empty, string.Empty, login.UserName);
|
||||||
|
if (usrResult is { Success: true, Value: not null })
|
||||||
|
user = usrResult.Value;
|
||||||
|
}
|
||||||
|
else if (!login.Email.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
usrResult = await _authDataRepository.GetUserAsync(string.Empty, login.Email, string.Empty);
|
||||||
|
if (usrResult is { Success: true, Value: not null })
|
||||||
|
user = usrResult.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return Result<AuthenticationStateModel>.Failed("Username & Email is empty, cannot login!");
|
||||||
|
|
||||||
|
if (user == null || !usrResult.Success)
|
||||||
|
return Result<AuthenticationStateModel>.Failed("No user found!");
|
||||||
|
|
||||||
|
if (user.PasswordHash != login.Password) //TODO: Hash password and compare
|
||||||
|
return Result<AuthenticationStateModel>.Failed("Login failed, invalid password.");
|
||||||
|
var state = new AuthenticationStateModel(user);
|
||||||
|
var authResult = await _authDataRepository.CreateAuthenticationStateAsync(state);
|
||||||
|
if (!authResult.Success)
|
||||||
|
return Result<AuthenticationStateModel>.Failed("Failed to store session to database!");
|
||||||
|
_dataCache.CacheSessionState(state);
|
||||||
|
return Result<AuthenticationStateModel>.Ok(state);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Failed to login!");
|
||||||
|
return Result<AuthenticationStateModel>.Failed("Login failed, exception thrown!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result> LogoutAsync(string state)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (state.IsNullOrWhiteSpace())
|
||||||
|
return Result.Failed($"Argument {nameof(state)} is empty!");
|
||||||
|
|
||||||
|
var stateResult = await _authDataRepository.GetAuthenticationStateAsync(state);
|
||||||
|
if (!stateResult.Success || stateResult.Value == null)
|
||||||
|
return stateResult;
|
||||||
|
var authState = stateResult.Value;
|
||||||
|
|
||||||
|
_dataCache.PurgeSessionState(state);
|
||||||
|
var updatedStateResult = await _authDataRepository.DeleteAuthenticationStateAsync(authState);
|
||||||
|
if (updatedStateResult.Success) return updatedStateResult;
|
||||||
|
_logger.Warning(updatedStateResult.Message);
|
||||||
|
return updatedStateResult;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Failed to logout!");
|
||||||
|
return Result.Failed("Failed to logout, exception thrown!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user