167 lines
5.9 KiB
C#
Executable File
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);
|
|
}
|
|
}
|
|
}
|