phronCare/phronCare.UIBlazor/Services/Authorization/CustomAuthorizationProvider.cs

165 lines
6.2 KiB
C#
Raw Normal View History

2025-01-24 19:17:26 -03:00
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.JSInterop;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json;
using phronCare.UIBlazor.Extensions;
namespace phronCare.UIBlazor.Services.Authorization
{
2025-04-28 18:43:59 -03:00
public class CustomAuthorizationProvider : AuthenticationStateProvider, ILoginService
2025-01-24 19:17:26 -03:00
{
private readonly IJSExtensions js;
private readonly HttpClient httpClient;
public static readonly string TOKENKEY = "phronCareTokenKey";
private AuthenticationState Anonimo => new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
2025-04-28 18:43:59 -03:00
public CustomAuthorizationProvider(IJSRuntime _js, HttpClient httpClient)
2025-01-24 19:17:26 -03:00
{
this.httpClient = httpClient;
js = new IJSExtensions(_js);
}
2025-04-28 18:43:59 -03:00
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
2025-01-24 19:17:26 -03:00
{
var token = await js.GetFromLocalStorage(TOKENKEY);
2025-04-28 18:43:59 -03:00
if (string.IsNullOrWhiteSpace(token) || !IsJwtFormat(token))
2025-01-24 19:17:26 -03:00
{
2025-04-28 18:43:59 -03:00
await js.RemoveItem(TOKENKEY);
2025-01-24 19:17:26 -03:00
return Anonimo;
}
2025-04-28 18:43:59 -03:00
2025-01-24 19:17:26 -03:00
return BuildAuthenticationState(token);
}
2025-04-28 18:43:59 -03:00
2025-01-24 19:17:26 -03:00
private AuthenticationState BuildAuthenticationState(string token)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt")));
}
2025-04-28 18:43:59 -03:00
2025-01-24 19:17:26 -03:00
public async Task<TokenData> GetTokenData()
{
2025-04-28 18:43:59 -03:00
var authState = await GetAuthenticationStateAsync();
var user = authState.User;
2025-01-24 19:17:26 -03:00
2025-04-28 18:43:59 -03:00
if (user.Identity is null || !user.Identity.IsAuthenticated)
throw new InvalidOperationException("Usuario no autenticado.");
2025-01-24 19:17:26 -03:00
2025-04-28 18:43:59 -03:00
var tokenString = await js.GetFromLocalStorage(TOKENKEY); // <-- ACA RECUPERAMOS EL JWT
return new TokenData
{
userName = user.Identity.Name ?? string.Empty,
role = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value ?? string.Empty,
token = tokenString ?? string.Empty, // <-- ACA SETEAMOS el token string
expiryTimeStamp = DateTime.UtcNow
};
2025-01-24 19:17:26 -03:00
}
2025-04-28 18:43:59 -03:00
//public async Task<TokenData> GetTokenData()
//{
// var authState = await GetAuthenticationStateAsync();
// var user = authState.User;
// if (user.Identity is null || !user.Identity.IsAuthenticated)
// throw new InvalidOperationException("Usuario no autenticado.");
// return new TokenData
// {
// userName = user.Identity.Name ?? string.Empty,
// role = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value ?? string.Empty,
// expiryTimeStamp = DateTime.UtcNow // Opcional: ajustar si extraes exp claim
// };
//}
2025-04-29 08:59:34 -03:00
//public async Task Login(string token)
//{
// await js.RemoveItem(TOKENKEY);
// await js.SetInLocalStorage(TOKENKEY, token);
// var authState = BuildAuthenticationState(token);
// NotifyAuthenticationStateChanged(Task.FromResult(authState));
//}
public async Task Login(string tokenJson)
2025-01-24 19:17:26 -03:00
{
2025-04-29 08:59:34 -03:00
var tokenData = JsonSerializer.Deserialize<TokenData>(tokenJson);
if (tokenData is null || string.IsNullOrWhiteSpace(tokenData.token))
throw new Exception("Token inválido o mal formado.");
2025-01-24 19:17:26 -03:00
await js.RemoveItem(TOKENKEY);
2025-04-29 08:59:34 -03:00
await js.SetInLocalStorage(TOKENKEY, tokenData.token); // ✔️ Guardamos solo el JWT string
var authState = BuildAuthenticationState(tokenData.token);
2025-01-24 19:17:26 -03:00
NotifyAuthenticationStateChanged(Task.FromResult(authState));
}
public async Task Logout()
{
httpClient.DefaultRequestHeaders.Authorization = null;
await js.RemoveItem(TOKENKEY);
NotifyAuthenticationStateChanged(Task.FromResult(Anonimo));
}
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
{
var claims = new List<Claim>();
var payload = jwt.Split('.')[1];
var jsonBytes = ParseBase64WithoutPadding(payload);
var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
if (keyValuePairs != null && keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles))
{
2025-04-28 18:43:59 -03:00
var rolesString = roles?.ToString() ?? string.Empty;
2025-01-24 19:17:26 -03:00
if (!string.IsNullOrEmpty(rolesString))
{
if (rolesString.Trim().StartsWith("["))
{
var parsedRoles = JsonSerializer.Deserialize<string[]>(rolesString) ?? Array.Empty<string>();
foreach (var parsedRole in parsedRoles)
{
claims.Add(new Claim(ClaimTypes.Role, parsedRole));
}
}
else
{
claims.Add(new Claim(ClaimTypes.Role, rolesString));
}
}
keyValuePairs.Remove(ClaimTypes.Role);
}
if (keyValuePairs != null)
{
claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value?.ToString() ?? "defaultValue")));
}
return claims;
}
2025-04-28 18:43:59 -03:00
2025-01-24 19:17:26 -03:00
private byte[] ParseBase64WithoutPadding(string base64)
{
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
2025-04-28 18:43:59 -03:00
private bool IsJwtFormat(string token)
{
return token?.Count(c => c == '.') == 2;
}
2025-01-24 19:17:26 -03:00
public class TokenData
{
public string userName { get; set; } = string.Empty;
public string role { get; set; } = string.Empty;
2025-04-28 18:43:59 -03:00
public string token { get; set; } = string.Empty;
2025-01-24 19:17:26 -03:00
public DateTime expiryTimeStamp { get; set; }
}
}
2025-04-28 18:43:59 -03:00
}