Добавьте файлы проекта.

This commit is contained in:
FrigaT
2026-04-13 14:16:44 +03:00
parent b2b5a3945a
commit 37c997dbe0
120 changed files with 5364 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
using PlaylistShared.Shared.DTO;
using System.Net.Http.Json;
namespace PlaylistShared.Pwa.Services;
public class ApiClient
{
private readonly HttpClient _http;
public ApiClient(HttpClient http) => _http = http;
public async Task<LoginResponse?> RefreshTokenAsync(string? refreshToken)
{
var response = await _http.PostAsJsonAsync("/api/account/refresh-token", new RefreshTokenRequest { RefreshToken = refreshToken });
if (!response.IsSuccessStatusCode) return null;
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<LoginResponse>>();
return apiResponse?.Data;
}
}

View File

@@ -0,0 +1,101 @@
using Microsoft.AspNetCore.Components.Authorization;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;
namespace PlaylistShared.Pwa.Services;
public class AuthStateProvider : AuthenticationStateProvider, IDisposable
{
private readonly TokenStorage _tokenStorage;
private readonly ApiClient _apiClient;
private readonly HttpClient _http;
private Timer? _refreshTimer;
private ClaimsPrincipal _currentUser = new(new ClaimsIdentity());
public AuthStateProvider(TokenStorage tokenStorage, ApiClient apiClient, HttpClient http)
{
_tokenStorage = tokenStorage;
_apiClient = apiClient;
_http = http;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var (token, refreshToken) = await _tokenStorage.GetTokensAsync();
if (string.IsNullOrEmpty(token))
return new AuthenticationState(_currentUser);
var principal = ParseToken(token);
if (principal == null)
return new AuthenticationState(_currentUser);
_currentUser = principal;
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
ScheduleTokenRefresh(token, refreshToken);
return new AuthenticationState(principal);
}
public async Task MarkUserAsAuthenticated(string token, string refreshToken)
{
await _tokenStorage.SetTokensAsync(token, refreshToken);
var principal = ParseToken(token);
_currentUser = principal ?? new ClaimsPrincipal(new ClaimsIdentity());
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public async Task MarkUserAsLoggedOut()
{
await _tokenStorage.ClearTokensAsync();
_currentUser = new ClaimsPrincipal(new ClaimsIdentity());
_http.DefaultRequestHeaders.Authorization = null;
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
private ClaimsPrincipal? ParseToken(string token)
{
try
{
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);
var identity = new ClaimsIdentity(jwt.Claims, "jwt");
return new ClaimsPrincipal(identity);
}
catch (Exception ex)
{
return null;
}
}
private void ScheduleTokenRefresh(string token, string? refreshToken)
{
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);
var expiresAt = jwt.ValidTo;
var timeToExpiry = expiresAt - DateTime.UtcNow;
var refreshTime = timeToExpiry - TimeSpan.FromMinutes(5);
if (refreshTime > TimeSpan.Zero && !string.IsNullOrEmpty(refreshToken))
{
_refreshTimer?.Dispose();
_refreshTimer = new Timer(async _ =>
{
try
{
var newToken = await _apiClient.RefreshTokenAsync(refreshToken);
if (newToken != null)
await MarkUserAsAuthenticated(newToken.Token, newToken.RefreshToken);
else
await MarkUserAsLoggedOut();
}
catch
{
await MarkUserAsLoggedOut();
}
}, null, (int)refreshTime.TotalMilliseconds, Timeout.Infinite);
}
}
public void Dispose() => _refreshTimer?.Dispose();
}

View File

@@ -0,0 +1,31 @@
using Microsoft.JSInterop;
namespace PlaylistShared.Pwa.Services;
public class TokenStorage
{
private readonly IJSRuntime _js;
private const string TokenKey = "jwt_token";
private const string RefreshTokenKey = "refresh_token";
public TokenStorage(IJSRuntime js) => _js = js;
public async Task SetTokensAsync(string token, string refreshToken)
{
await _js.InvokeVoidAsync("localStorage.setItem", TokenKey, token);
await _js.InvokeVoidAsync("localStorage.setItem", RefreshTokenKey, refreshToken);
}
public async Task<(string? token, string? refreshToken)> GetTokensAsync()
{
var token = await _js.InvokeAsync<string>("localStorage.getItem", TokenKey);
var refreshToken = await _js.InvokeAsync<string>("localStorage.getItem", RefreshTokenKey);
return (token, refreshToken);
}
public async Task ClearTokensAsync()
{
await _js.InvokeVoidAsync("localStorage.removeItem", TokenKey);
await _js.InvokeVoidAsync("localStorage.removeItem", RefreshTokenKey);
}
}