Initial commit - Auth.RCL
This commit is contained in:
Executable
+166
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+282
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user