Auth System

This commit is contained in:
Andre Beging
2022-04-04 10:04:19 +02:00
parent 77382944eb
commit 427b924759
14 changed files with 552 additions and 16 deletions

View File

@@ -0,0 +1,133 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace FoodsharingSiegen.Server.Auth
{
/// <summary>
/// The auth helper class (a. beging, 04.04.2022)
/// </summary>
public static class AuthHelper
{
#region Public Method Decrypt
/// <summary>
/// Decrypts the crypted text (a. beging, 04.04.2022)
/// </summary>
/// <param name="cryptedText">The crypted text</param>
/// <returns>The string</returns>
public static string Decrypt(string cryptedText)
{
CreateAlgorithm(out var tripleDes);
var toEncryptArray = Convert.FromBase64String(cryptedText);
var cTransform = tripleDes.CreateDecryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tripleDes.Clear();
return Encoding.UTF8.GetString(resultArray);
}
#endregion
#region Public Method Encrypt
/// <summary>
/// Encrypts the plain text (a. beging, 04.04.2022)
/// </summary>
/// <param name="plainText">The plain text</param>
/// <returns>The string</returns>
public static string Encrypt(string plainText)
{
CreateAlgorithm(out var tripleDes);
var toEncryptArray = Encoding.UTF8.GetBytes(plainText );
var cTransform = tripleDes.CreateEncryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tripleDes.Clear();
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
#endregion
#region Public Method GetSigningKey
/// <summary>
/// Gets the signing key (a. beging, 04.04.2022)
/// </summary>
/// <returns>The security key</returns>
public static SecurityKey GetSigningKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey));
#endregion
#region Public Method ValidateToken
/// <summary>
/// Validates the token using the specified token (a. beging, 04.04.2022)
/// </summary>
/// <param name="token">The token</param>
/// <returns>A task containing the bool</returns>
public static async Task<bool> ValidateToken(string? token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var result = await tokenHandler.ValidateTokenAsync(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = GetSigningKey(),
ValidateAudience = true,
ValidAudience = "FS-Siegen",
ValidateIssuer = true,
ValidIssuer = "FS-Siegen"
});
return result.IsValid;
}
catch (Exception e)
{
return false;
}
}
#endregion
#region Private Method CreateAlgorithm
/// <summary>
/// Creates the algorithm using the specified triple des (a. beging, 04.04.2022)
/// </summary>
/// <param name="tripleDes">The triple des</param>
private static void CreateAlgorithm(out TripleDES tripleDes)
{
var md5 = MD5.Create();
var keyArray = md5.ComputeHash(Encoding.UTF8.GetBytes(SigningKey));
md5.Clear();
tripleDes = TripleDES.Create();
tripleDes.Key = keyArray;
tripleDes.Mode = CipherMode.ECB;
tripleDes.Padding = PaddingMode.PKCS7;
}
#endregion
/// <summary>
/// The signing key
/// </summary>
public const string SigningKey = "2uasw2§$%1nd47n9s43&%Zs3529s23&/%AW";
}
}

View File

@@ -0,0 +1,147 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Server.Data;
using FoodsharingSiegen.Server.Data.Service;
using FoodsharingSiegen.Server.Service;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
namespace FoodsharingSiegen.Server.Auth
{
/// <summary>
/// The auth service class (a. beging, 04.04.2022)
/// </summary>
/// <seealso cref="ServiceBase"/>
public class AuthService : ServiceBase
{
#region Public Properties
/// <summary>
/// Gets or sets the value of the user (ab)
/// </summary>
public User? User { get; set; }
#endregion
#region Private Fields
/// <summary>
/// The authentication state provider
/// </summary>
private readonly AuthenticationStateProvider _authenticationStateProvider;
/// <summary>
/// The local storage service
/// </summary>
private readonly LocalStorageService _localStorageService;
#endregion
#region Setup/Teardown
/// <summary>
/// Initializes a new instance of the <see cref="AuthService"/> class
/// </summary>
/// <param name="context">The context</param>
/// <param name="localStorageService">The local storage service</param>
/// <param name="authenticationStateProvider">The authentication state provider</param>
public AuthService(FsContext context, LocalStorageService localStorageService, AuthenticationStateProvider authenticationStateProvider) : base(context)
{
_localStorageService = localStorageService;
_authenticationStateProvider = authenticationStateProvider;
}
#endregion
#region Public Method Login
/// <summary>
/// Logins the mail address (a. beging, 04.04.2022)
/// </summary>
/// <param name="mailAddress">The mail address</param>
/// <param name="password">The password</param>
/// <returns>A task containing the operation result</returns>
public async Task<OperationResult> Login(string mailAddress, string password)
{
#region Ensure Admin
var existingTroogS = await Context.Users.AnyAsync(x => x.Mail == "fs@beging.de");
if (!existingTroogS)
{
var troogs = new User
{
Name = "Andre",
Mail = "fs@beging.de",
Type = UserType.Admin,
Created = DateTime.UtcNow,
EncryptedPassword = "qSIxTZo7J8M="
};
await Context.Users.AddAsync(troogs);
await Context.SaveChangesAsync();
}
#endregion Ensure Admin
var encryptedPassword = AuthHelper.Encrypt(password);
User = await Context.Users.FirstOrDefaultAsync(x => x.Mail.ToLower() == mailAddress.ToLower() && x.EncryptedPassword == encryptedPassword);
if (User != null)
{
// Daten korrekt
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, User.Id.ToString()),
}),
Expires = DateTime.UtcNow.AddDays(30),
Issuer = "FS-Siegen",
Audience = "FS-Siegen",
SigningCredentials = new SigningCredentials(AuthHelper.GetSigningKey(), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var serializedToken = tokenHandler.WriteToken(token);
await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken);
return new OperationResult();
}
return new OperationResult(new Exception("Invalid"));
}
#endregion
#region Public Method Logout
/// <summary>
/// Logouts this instance (a. beging, 04.04.2022)
/// </summary>
/// <returns>A task containing the operation result</returns>
public async Task<OperationResult> Logout()
{
try
{
await _localStorageService.RemoveItem(StorageKeys.TokenKey);
User = null;
((TokenAuthStateProvider) _authenticationStateProvider).MarkUserAsLoggedOut();
return new OperationResult();
}
catch (Exception e)
{
return new OperationResult(e);
}
}
#endregion
}
}

View File

@@ -0,0 +1,78 @@
using System.Text.Json;
using Microsoft.JSInterop;
namespace FoodsharingSiegen.Server.Service
{
/// <summary>
/// The local storage service class (a. beging, 02.04.2022)
/// </summary>
public class LocalStorageService
{
#region Private Fields
/// <summary>
/// The js runtime
/// </summary>
private readonly IJSRuntime _jsRuntime;
#endregion
#region Setup/Teardown
/// <summary>
/// Constructor
/// </summary>
/// <param name="jsRuntime"></param>
public LocalStorageService(IJSRuntime jsRuntime) => _jsRuntime = jsRuntime;
#endregion
#region Public Method GetItem
/// <summary>
/// Ein Item aus dem LocalStorage laden
/// </summary>
/// <param name="key">Der Key des Items</param>
/// <typeparam name="T">Typ des Item</typeparam>
/// <returns></returns>
public async Task<T?> GetItem<T>(string key)
{
var json = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
if (json == null)
return default;
return JsonSerializer.Deserialize<T>(json);
}
#endregion
#region Public Method RemoveItem
/// <summary>
/// Ein Item aus dem LocalStorage löschen
/// </summary>
/// <param name="key">Der Key des Items</param>
public async Task RemoveItem(string key)
{
await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key);
}
#endregion
#region Public Method SetItem
/// <summary>
/// Ein Item in den LocalStorage schreiben
/// </summary>
/// <param name="key">Der Key des Items</param>
/// <param name="value">Das Item</param>
/// <typeparam name="T">Typ des Item</typeparam>
public async Task SetItem<T>(string key, T value)
{
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, JsonSerializer.Serialize(value));
}
#endregion
}
}

View File

@@ -0,0 +1,79 @@
using System.Security.Claims;
using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Server.Auth;
using Microsoft.AspNetCore.Components.Authorization;
namespace FoodsharingSiegen.Server.Service
{
/// <summary>
/// The token auth state provider class (a. beging, 02.04.2022)
/// </summary>
/// <seealso cref="AuthenticationStateProvider"/>
public class TokenAuthStateProvider : AuthenticationStateProvider
{
#region Private Fields
/// <summary> LocalStorageService </summary>
private readonly LocalStorageService _localStorageService;
#endregion
#region Setup/Teardown
/// <summary>
/// Constructor
/// </summary>
/// <param name="localStorageService"></param>
public TokenAuthStateProvider(LocalStorageService localStorageService) => _localStorageService = localStorageService;
#endregion
#region Override GetAuthenticationStateAsync
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Get the current authenticationstate </summary>
/// <remarks> A. Beging, 02.02.2022. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var token = await _localStorageService.GetItem<string>(StorageKeys.TokenKey);
var tokenValid = await AuthHelper.ValidateToken(token);
var identity = new ClaimsIdentity();
if (tokenValid)
identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "user")
}, "TODO");
var claimsPrincipal = new ClaimsPrincipal(identity);
return new AuthenticationState(claimsPrincipal);
}
#endregion
#region Public Method MarkUserAsAuthenticated
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Mark user as authenticated. </summary>
/// <remarks> A. Beging, 02.02.2022. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void MarkUserAsAuthenticated() => NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
#endregion
#region Public Method MarkUserAsLoggedOut
/// <summary>
/// Marks the user as logged out (a. beging, 02.04.2022)
/// </summary>
public void MarkUserAsLoggedOut()
{
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymousUser));
NotifyAuthenticationStateChanged(authState);
}
#endregion
}
}