Add authentication and authorization (#21)

Reviewed-on: #21
This commit was merged in pull request #21.
This commit is contained in:
2025-04-21 01:53:58 +02:00
parent 51dab81121
commit 033747899b
55 changed files with 2186 additions and 317 deletions

View File

@@ -3,10 +3,15 @@ using System.Net.Http.Headers;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text;
using Blazored.LocalStorage;
using GameIdeas.Shared.Constants;
namespace GameIdeas.BlazorApp.Services;
public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory) : IHttpClientService
public class HttpClientService(
IHttpClientFactory httpClientFactory,
ILoggerFactory loggerFactory,
ILocalStorageService localStorage) : IHttpClientService
{
private readonly HttpClient httpClient = httpClientFactory.CreateClient("GameIdeas.WebAPI");
private readonly ILogger<HttpClientService> logger = loggerFactory.CreateLogger<HttpClientService>();
@@ -25,6 +30,8 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
public async Task<T?> PostAsync<T>(string url, object data)
{
await SetAuthorizationHeader();
var jsonContent = JsonSerializer.Serialize(data, _optionsCamelCase);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(url, content);
@@ -32,8 +39,11 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
return await GetResultValue<T>(response, ResourcesKey.ErrorWhenPostingData);
}
public async Task<T?> PutAsync<T>(string url, object data)
{
await SetAuthorizationHeader();
var jsonContent = JsonSerializer.Serialize(data, _optionsCamelCase);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync(url, content);
@@ -43,6 +53,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
public async Task<T?> DeleteAsync<T>(string? url)
{
await SetAuthorizationHeader();
var response = await httpClient.DeleteAsync(url);
return await GetResultValue<T>(response, ResourcesKey.ErrorWhenDeletingData);
@@ -50,6 +61,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
public async Task<T?> FetchDataAsync<T>(string? url)
{
await SetAuthorizationHeader();
var response = await httpClient.GetAsync(url);
return await GetResultValue<T>(response, ResourcesKey.ErrorWhenFetchingData);
@@ -57,6 +69,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
public async Task<byte[]?> FetchBytesAsync(string? url)
{
await SetAuthorizationHeader();
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
@@ -71,6 +84,7 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
public async Task<Stream?> FetchStreamAsync(string? url)
{
await SetAuthorizationHeader();
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
@@ -84,6 +98,8 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
public async Task<T?> PostFileAsync<T>(string? url, Stream fileStream, string fileName, string contentType)
{
await SetAuthorizationHeader();
using var content = new MultipartFormDataContent();
var streamContent = new StreamContent(fileStream);
@@ -122,4 +138,11 @@ public class HttpClientService(IHttpClientFactory httpClientFactory, ILoggerFact
throw new HttpRequestException(
$"{errorMessage} + StatusCode: {response.StatusCode} + Reason: {response.ReasonPhrase}");
}
private async Task SetAuthorizationHeader()
{
var token = await localStorage.GetItemAsStringAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("bearer", token);
}
}

View File

@@ -0,0 +1,48 @@
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using GameIdeas.Shared.Constants;
namespace GameIdeas.BlazorApp.Services;
public class JwtAuthenticationStateProvider(ILocalStorageService localStorage) : AuthenticationStateProvider
{
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var savedToken = await localStorage.GetItemAsStringAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
if (!string.IsNullOrWhiteSpace(savedToken))
{
try
{
var token = new JwtSecurityTokenHandler().ReadJwtToken(savedToken);
return new AuthenticationState(
new ClaimsPrincipal(new ClaimsIdentity(token.Claims, "jwt"))
);
}
catch
{
await localStorage.RemoveItemAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
}
}
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
public async Task NotifyUserAuthenticationAsync(string token)
{
await localStorage.SetItemAsStringAsync(GlobalConstants.LS_AUTH_STORAGE_KEY, token);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public async Task NotifyUserLogoutAsync()
{
await localStorage.RemoveItemAsync(GlobalConstants.LS_AUTH_STORAGE_KEY);
var nobody = new ClaimsPrincipal(new ClaimsIdentity());
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(nobody)));
}
}