Auth System
This commit is contained in:
133
FoodsharingSiegen.Server/Auth/AuthHelper.cs
Normal file
133
FoodsharingSiegen.Server/Auth/AuthHelper.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
147
FoodsharingSiegen.Server/Auth/AuthService.cs
Normal file
147
FoodsharingSiegen.Server/Auth/AuthService.cs
Normal 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
|
||||
}
|
||||
}
|
||||
78
FoodsharingSiegen.Server/Auth/LocalStorageService.cs
Normal file
78
FoodsharingSiegen.Server/Auth/LocalStorageService.cs
Normal 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
|
||||
}
|
||||
}
|
||||
79
FoodsharingSiegen.Server/Auth/TokenAuthStateProvider.cs
Normal file
79
FoodsharingSiegen.Server/Auth/TokenAuthStateProvider.cs
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user