Files
Auth.RCL/Services/ApiAuthenticationStateProvider.cs
T

167 lines
5.9 KiB
C#
Executable File

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Auth.Contracts.DTOs.Auth;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Authorization;
namespace Auth.RCL.Services
{
public class ApiAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;
public ApiAuthenticationStateProvider(
HttpClient httpClient,
ILocalStorageService localStorage
)
{
_httpClient = httpClient;
_localStorage = localStorage;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var refreshToken = await _localStorage.GetItemAsync<string>("refreshToken");
var accessToken = await _localStorage.GetItemAsync<string>("authToken");
var expiryDate = await _localStorage.GetItemAsync<string>("expiryDate");
if (string.IsNullOrWhiteSpace(accessToken))
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
if (DateTime.Now.AddMinutes(4) > DateTime.Parse(expiryDate))
{
var tokenModel = JsonSerializer.Serialize(
new TokenModel()
{
RefreshToken = refreshToken,
AccessToken = accessToken,
Expiry = new DateTime(),
}
);
var response = await _httpClient.PostAsync(
"auth/refresh",
new StringContent(tokenModel, Encoding.UTF8, "application/json")
);
if (!response.IsSuccessStatusCode)
{
_httpClient.DefaultRequestHeaders.Authorization = null;
MarkUserAsLoggedOut();
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
var loginResult = JsonSerializer.Deserialize<LoginResult>(
await response.Content.ReadAsStringAsync(),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
await _localStorage.SetItemAsync("authToken", loginResult.AccessToken);
await _localStorage.SetItemAsync("refreshToken", loginResult.RefreshToken);
await _localStorage.SetItemAsync("expiryDate", loginResult.Expiry);
accessToken = loginResult.AccessToken;
}
// if (string.IsNullOrWhiteSpace(savedToken) || DateTime.Now > DateTime.Parse(expiryDate))
// {
// return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
// }
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer",
accessToken
);
var claims = ParseClaimsFromJwt(accessToken);
if (claims is null)
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt")));
}
public async void MarkUserAsAuthenticated(string jwtToken)
{
var claims = ParseClaimsFromJwt(jwtToken);
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
// var userId = authenticatedUser.Claims.FirstOrDefault(x => x.Type == "uid").Value;
}
public async Task MarkUserAsLoggedOut()
{
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymousUser));
NotifyAuthenticationStateChanged(authState);
_httpClient.DefaultRequestHeaders.Authorization = null;
}
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
{
try
{
var claims = new List<Claim>();
var payload = jwt.Split('.')[1];
var jsonBytes = ParseBase64WithoutPadding(payload);
var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(
jsonBytes
);
keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);
if (roles != null)
{
if (roles.ToString().Trim().StartsWith("["))
{
var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());
foreach (var parsedRole in parsedRoles)
{
claims.Add(new Claim(ClaimTypes.Role, parsedRole));
}
}
else
{
claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
}
keyValuePairs.Remove(ClaimTypes.Role);
}
claims.AddRange(
keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()))
);
return claims;
}
catch
{
return null;
}
}
private byte[] ParseBase64WithoutPadding(string base64)
{
switch (base64.Length % 4)
{
case 2:
base64 += "==";
break;
case 3:
base64 += "=";
break;
}
return Convert.FromBase64String(base64);
}
}
}