Добавьте файлы проекта.
This commit is contained in:
19
PlaylistShared.Pwa/Services/ApiClient.cs
Normal file
19
PlaylistShared.Pwa/Services/ApiClient.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
101
PlaylistShared.Pwa/Services/AuthStateProvider.cs
Normal file
101
PlaylistShared.Pwa/Services/AuthStateProvider.cs
Normal 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();
|
||||
}
|
||||
31
PlaylistShared.Pwa/Services/TokenStorage.cs
Normal file
31
PlaylistShared.Pwa/Services/TokenStorage.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user