Initial commit - Auth.RCL

This commit is contained in:
2026-08-05 21:16:00 +03:00
commit 062ff0146a
231 changed files with 15819 additions and 0 deletions
+166
View File
@@ -0,0 +1,166 @@
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);
}
}
}
+140
View File
@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Json;
using System.Threading.Tasks;
// using Microsoft.Extensions.DependencyInjection; // For AddHttpClient(), AddHttpClient<T>
using System.Net.Http;
using Auth.Contracts.DTOs.Users; // For RoleVM
namespace Auth.RCL.Services
{
public class RoleAPIConsumer
{
private readonly HttpClient client;
// public RealEstateFilter RealEstateFilter { get; set; } = new();
public RoleAPIConsumer(IHttpClientFactory httpClientFactory)
{
this.client = httpClientFactory.CreateClient("Auth");
}
public async Task<bool> RemoveUserFromRole(string userId, string roleName)
{
try
{
// query = query + $"Language={appState.Language}";
// var response = await client.GetFromJsonAsync<List<Product>>($"{query}");
var response = await client.DeleteAsync($"role-management/{roleName}/user/{userId}");
return response.IsSuccessStatusCode;
}
catch (HttpRequestException ex)
{
// _logger.LogError(ex, "GET Role failed for endpoint: {Endpoint}", endpoint);
throw;
}
}
public async Task<bool> AddUserToRole(string userId, string roleName)
{
try
{
var response = await client.PostAsync(
$"role-management/{roleName}/user/{userId}",
null
);
return response.IsSuccessStatusCode;
// return await response.Content.ReadFromJsonAsync<RequestVM>();
}
catch (HttpRequestException ex)
{
// _logger.LogError(ex, "GET Role failed for endpoint: {Endpoint}", endpoint);
throw;
}
}
public async Task<List<RoleVM>> GetRoles()
{
try
{
return await client.GetFromJsonAsync<List<RoleVM>>("role-management/roles");
}
catch (HttpRequestException)
{
throw;
}
}
public async Task<bool> CreateRole(RoleVM role)
{
try
{
var response = await client.PostAsJsonAsync("role-management/roles", role);
return response.IsSuccessStatusCode;
}
catch (HttpRequestException)
{
throw;
}
}
public async Task<bool> DeleteRole(string roleId)
{
try
{
var response = await client.DeleteAsync($"role-management/roles/{roleId}");
return response.IsSuccessStatusCode;
}
catch (HttpRequestException)
{
throw;
}
}
public async Task<List<string>> GetRolePermissions(string roleId)
{
try
{
return await client.GetFromJsonAsync<List<string>>($"role-management/roles/{roleId}/permissions");
}
catch (HttpRequestException)
{
throw;
}
}
public async Task<bool> AddPermissionToRole(string roleId, string permission)
{
try
{
var response = await client.PostAsJsonAsync($"role-management/roles/{roleId}/permissions", permission);
return response.IsSuccessStatusCode;
}
catch (HttpRequestException)
{
throw;
}
}
public async Task<bool> RemovePermissionFromRole(string roleId, string permission)
{
try
{
// Send as JSON body for DELETE request
var request = new HttpRequestMessage(HttpMethod.Delete, $"role-management/roles/{roleId}/permissions")
{
Content = JsonContent.Create(permission)
};
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode;
}
catch (HttpRequestException)
{
throw;
}
}
}
}
+282
View File
@@ -0,0 +1,282 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Auth.Contracts.DTOs.Auth;
using Auth.Contracts.DTOs.Users;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Authorization;
namespace Auth.RCL.Services
{
public class UserAPIConsumer
{
private readonly HttpClient client;
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly ILocalStorageService _localStorage;
public UserAPIConsumer(
IHttpClientFactory httpClientFactory,
AuthenticationStateProvider authenticationStateProvider,
ILocalStorageService localStorage
)
{
this.client = httpClientFactory.CreateClient("Auth");
_authenticationStateProvider = authenticationStateProvider;
_localStorage = localStorage;
}
public async Task<(bool IsSuccessful, string Message)> Register(RegisterModel registerModel)
{
try
{
var registerAsJson = JsonSerializer.Serialize(registerModel);
var response = await client.PostAsync(
"auth/Register",
new StringContent(registerAsJson, Encoding.UTF8, "application/json")
);
return (response.IsSuccessStatusCode, await response.Content.ReadAsStringAsync());
}
catch (HttpRequestException ex)
{
// _logger.LogError(ex, "GET request failed for endpoint: {Endpoint}", endpoint);
throw;
}
}
public async Task RefreshToken(TokenModel tokenModel) { }
public async Task<bool> Login(TokenRequestModel tokenRequestModel)
{
try
{
var loginAsJson = JsonSerializer.Serialize(tokenRequestModel);
var response = await client.PostAsync(
"auth/login",
new StringContent(loginAsJson, Encoding.UTF8, "application/json")
);
if (response.IsSuccessStatusCode)
{
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);
(
(ApiAuthenticationStateProvider)_authenticationStateProvider
).MarkUserAsAuthenticated(loginResult.AccessToken);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"bearer",
loginResult.AccessToken
);
loginResult.IsSuccessful = true;
return true;
}
return false;
}
catch (HttpRequestException ex)
{
// _logger.LogError(ex, "GET request failed for endpoint: {Endpoint}", endpoint);
throw;
}
}
public async Task Logout()
{
try
{
try
{
await client.PostAsync("auth/logout", null);
}
catch { }
await _localStorage.RemoveItemAsync("authToken");
await _localStorage.RemoveItemAsync("refreshToken");
await _localStorage.RemoveItemAsync("expiryDate");
(
(ApiAuthenticationStateProvider)_authenticationStateProvider
).MarkUserAsLoggedOut();
client.DefaultRequestHeaders.Authorization = null;
}
catch
{
throw;
}
}
public async Task<UserVM> GetUserInfoAsync(string id)
{
try
{
var user = await client.GetFromJsonAsync<UserVM>($"user/{id}");
return user;
}
catch
{
throw;
}
}
public async Task<bool> UpdateUserInfoAsync(UserVM user)
{
try
{
var response = await client.PutAsJsonAsync("user", user);
return response.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<List<UserVM>> GetUsers()
{
try
{
var users = await client.GetFromJsonAsync<List<UserVM>>($"users");
return users;
}
catch
{
throw;
}
}
public async Task<UserVM> GetUser(string id)
{
try
{
var user = await client.GetFromJsonAsync<UserVM>($"user/{id}");
return user;
}
catch
{
throw;
}
}
public async Task<bool> UpdateUser(UserInfoUpdate user)
{
try
{
var response = await client.PutAsJsonAsync("user", user);
return response.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> ResendConfirmation()
{
try
{
var response = await client.GetAsync("auth/resend-confirmation");
return response.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> RequestResetPassword(string email)
{
try
{
var response = await client.GetAsync($"auth/password-reset-token/{email}");
return response.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> ConfirmEmail(string token)
{
try
{
var response = await client.PostAsJsonAsync(
"auth/confirm-email",
token,
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }
);
return response.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> ResetPassword(PasswordResetModel passwordResetModel)
{
try
{
var response = await client.PostAsJsonAsync(
"auth/reset-password",
passwordResetModel,
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }
);
return response.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> SendPhoneNumberToken(string phoneNumber)
{
try
{
var result = await client.PostAsJsonAsync($"auth/phone-number", phoneNumber);
return result.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> UpdatePhoneNumber(UpdatePhoneNumber updatePhoneNumber)
{
try
{
var result = await client.PutAsJsonAsync($"auth/phone-number", updatePhoneNumber);
return result.IsSuccessStatusCode;
}
catch
{
throw;
}
}
public async Task<bool> SendChangeEmailToken(RequestChangeEmail requestChangeEmail)
{
try
{
var result = await client.PostAsJsonAsync($"auth/change-email", requestChangeEmail);
return result.IsSuccessStatusCode;
}
catch
{
throw;
}
}
}
}
+76
View File
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Generic.Contracts.Generics;
namespace Auth.RCL.Services.Utils
{
public class QueryBuilder
{
private readonly StringBuilder _builder;
public QueryBuilder()
{
_builder = new StringBuilder();
}
public QueryBuilder AddBase(string baseString)
{
_builder.Insert(0, baseString);
return this;
}
public QueryBuilder AddPagination(Pagination pagination)
{
if (pagination != null)
{
AppendParameter("currentPage", pagination.CurrentPage.Value);
AppendParameter("pageSize", pagination.PageSize.Value);
}
return this;
}
public QueryBuilder AddSort(SortModel sort)
{
if (sort != null && !string.IsNullOrEmpty(sort.SortBy))
{
AppendParameter("Column", sort.SortBy);
AppendParameter("sortDirection", sort.SortDirection.ToString());
}
return this;
}
public QueryBuilder AddModelProperties<TModel>(TModel model)
{
if (model == null)
return this;
var properties = typeof(TModel).GetProperties();
foreach (var prop in properties)
{
var value = prop.GetValue(model)?.ToString();
if (!string.IsNullOrEmpty(value))
{
AppendParameter(prop.Name.ToLower(), value);
}
}
return this;
}
private void AppendParameter(string key, string value)
{
if (_builder.Length > 0)
_builder.Append('&');
_builder.Append($"{Uri.EscapeDataString(key)}={Uri.EscapeDataString(value)}");
}
private void AppendParameter(string key, int value)
{
AppendParameter(key, value.ToString());
}
public override string ToString() => _builder.ToString();
}
}