188 lines
7.5 KiB
C#
188 lines
7.5 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
using Blazored.SessionStorage;
|
|
|
|
using MesaFabApproval.Shared.Models;
|
|
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
|
|
namespace MesaFabApproval.Client.Services;
|
|
|
|
public interface IAuthenticationService {
|
|
Task<ClaimsPrincipal> SendAuthenticationRequest(string loginId, string password);
|
|
Task<ClaimsPrincipal> FetchAuthState();
|
|
Task ClearTokens();
|
|
Task ClearCurrentUser();
|
|
Task SetTokens(string jwt, string refreshToken);
|
|
Task SetLoginId(string loginId);
|
|
Task SetCurrentUser(User user);
|
|
Task<User> GetCurrentUser();
|
|
Task<AuthTokens> GetAuthTokens();
|
|
Task<string> GetLoginId();
|
|
ClaimsPrincipal GetClaimsPrincipalFromJwt(string jwt);
|
|
}
|
|
|
|
public class AuthenticationService : IAuthenticationService {
|
|
private readonly ISessionStorageService _sessionStorageService;
|
|
private readonly IMemoryCache _cache;
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
|
|
public AuthenticationService(ISessionStorageService sessionStorageService,
|
|
IMemoryCache cache,
|
|
IHttpClientFactory httpClientFactory) {
|
|
_sessionStorageService = sessionStorageService ??
|
|
throw new ArgumentNullException("ISessionStorageService not injected");
|
|
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
|
|
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected");
|
|
}
|
|
|
|
public async Task<ClaimsPrincipal> SendAuthenticationRequest(string loginId, string password) {
|
|
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentException("loginId cannot be null or empty");
|
|
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("password cannot be null or empty");
|
|
|
|
HttpClient httpClient = _httpClientFactory.CreateClient("API");
|
|
|
|
AuthAttempt authAttempt = new() {
|
|
LoginID = loginId,
|
|
Password = password
|
|
};
|
|
|
|
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/login");
|
|
|
|
request.Content = new StringContent(JsonSerializer.Serialize(authAttempt),
|
|
Encoding.UTF8,
|
|
"application/json");
|
|
|
|
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
|
|
|
|
if (httpResponseMessage.IsSuccessStatusCode) {
|
|
string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
|
|
|
|
JsonSerializerOptions jsonSerializerOptions = new() {
|
|
PropertyNameCaseInsensitive = true
|
|
};
|
|
|
|
LoginResult loginResult = JsonSerializer.Deserialize<LoginResult>(responseContent, jsonSerializerOptions) ??
|
|
throw new Exception("Unable to parse login result from API response");
|
|
|
|
if (!loginResult.IsAuthenticated) throw new Exception($"User with Login ID {loginId} not authorized");
|
|
|
|
await SetLoginId(loginId);
|
|
|
|
await SetTokens(loginResult.AuthTokens.JwtToken, loginResult.AuthTokens.RefreshToken);
|
|
|
|
await SetCurrentUser(loginResult.User);
|
|
|
|
ClaimsPrincipal principal = GetClaimsPrincipalFromJwt(loginResult.AuthTokens.JwtToken);
|
|
|
|
return principal;
|
|
} else {
|
|
throw new Exception($"Login API request failed for {loginId}, because {httpResponseMessage.ReasonPhrase}");
|
|
}
|
|
}
|
|
|
|
public async Task<ClaimsPrincipal> FetchAuthState() {
|
|
string? jwt = _cache.Get<string>("MesaFabApprovalJwt");
|
|
|
|
if (jwt is null) jwt = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalJwt");
|
|
|
|
if (jwt is null) throw new Exception("Unable to find JWT");
|
|
|
|
ClaimsPrincipal principal = GetClaimsPrincipalFromJwt(jwt);
|
|
|
|
return principal;
|
|
}
|
|
|
|
public async Task ClearTokens() {
|
|
_cache.Remove("MesaFabApprovalJwt");
|
|
await _sessionStorageService.RemoveItemAsync("MesaFabApprovalJwt");
|
|
_cache.Remove("MesaFabApprovalRefreshToken");
|
|
await _sessionStorageService.RemoveItemAsync("MesaFabApprovalRefreshToken");
|
|
}
|
|
|
|
public async Task SetTokens(string jwt, string refreshToken) {
|
|
if (string.IsNullOrWhiteSpace(jwt)) throw new ArgumentNullException("JWT cannot be null or empty");
|
|
if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException("Refresh token cannot be null or empty");
|
|
|
|
_cache.Set<string>("MesaFabApprovalJwt", jwt);
|
|
await _sessionStorageService.SetItemAsync<string>("MesaFabApprovalJwt", jwt);
|
|
_cache.Set<string>("MesaFabApprovalRefreshToken", refreshToken);
|
|
await _sessionStorageService.SetItemAsync<string>("MesaFabApprovalRefreshToken", refreshToken);
|
|
}
|
|
|
|
public async Task SetLoginId(string loginId) {
|
|
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentNullException("LoginId cannot be null or empty");
|
|
|
|
_cache.Set<string>("MesaFabApprovalUserId", loginId);
|
|
await _sessionStorageService.SetItemAsync<string>("MesaFabApprovalUserId", loginId);
|
|
}
|
|
|
|
public async Task SetCurrentUser(User user) {
|
|
if (user is null) throw new ArgumentNullException("User cannot be null");
|
|
|
|
_cache.Set<User>("MesaFabApprovalCurrentUser", user);
|
|
await _sessionStorageService.SetItemAsync<User>("MesaFabApprovalCurrentUser", user);
|
|
}
|
|
|
|
public async Task ClearCurrentUser() {
|
|
_cache.Remove("MesaFabApprovalCurrentUser");
|
|
await _sessionStorageService.RemoveItemAsync("MesaFabApprovalCurrentUser");
|
|
_cache.Remove("MesaFabApprovalUserId");
|
|
await _sessionStorageService.RemoveItemAsync("MesaFabApprovalUserId");
|
|
}
|
|
|
|
public async Task<User> GetCurrentUser() {
|
|
User? currentUser = null;
|
|
|
|
currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser") ??
|
|
await _sessionStorageService.GetItemAsync<User>("MesaFabApprovalCurrentUser");
|
|
|
|
return currentUser;
|
|
}
|
|
|
|
public async Task<AuthTokens> GetAuthTokens() {
|
|
AuthTokens? authTokens = null;
|
|
|
|
string? jwt = _cache.Get<string>("MesaFabApprovalJwt");
|
|
if (jwt is null) jwt = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalJwt");
|
|
|
|
string? refreshToken = _cache.Get<string>("MesaFabApprovalRefreshToken");
|
|
if (refreshToken is null) refreshToken = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalRefreshToken");
|
|
|
|
if (!string.IsNullOrWhiteSpace(jwt) && !string.IsNullOrWhiteSpace(refreshToken)) {
|
|
authTokens = new() {
|
|
JwtToken = jwt,
|
|
RefreshToken = refreshToken
|
|
};
|
|
}
|
|
|
|
return authTokens;
|
|
}
|
|
|
|
public async Task<string> GetLoginId() {
|
|
string? loginId = _cache.Get<string>("MesaFabApprovalUserId");
|
|
if (loginId is null) loginId = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalUserId");
|
|
|
|
return loginId;
|
|
}
|
|
|
|
public ClaimsPrincipal GetClaimsPrincipalFromJwt(string jwt) {
|
|
if (string.IsNullOrWhiteSpace(jwt)) throw new ArgumentException("JWT cannot be null or empty");
|
|
|
|
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
|
|
|
|
if (!tokenHandler.CanReadToken(jwt)) {
|
|
throw new Exception("Unable to parse JWT from API");
|
|
}
|
|
|
|
JwtSecurityToken jwtSecurityToken = tokenHandler.ReadJwtToken(jwt);
|
|
|
|
ClaimsIdentity identity = new ClaimsIdentity(jwtSecurityToken.Claims, "MesaFabApprovalWasm");
|
|
|
|
return new(identity);
|
|
}
|
|
}
|