Add Blazor WebAssembly Admin Page (#426)
* Added Blazor Client Configured PublicAPI CORS to allow traffic from client * Make admin page home page; remove extra pages Add CatalogType list endpoint * Wired up Types and Brands in the API and the admin list page * Adding a custom HttpClient to talk securely to API * Ardalis/blazor (#419) * Login added * AuthService will handel http request secure and not secure. * Logout added * CatalogBrandService in it is own service * Get token from localstorage when refresh. * used GetAsync * Fixed Login and Logout switch. * CatalogItemService added * CatalogTypeService added & Auth for CatalogType. using not used removed. * Made BlazorComponent and BlazorLayoutComponent for refresh. Index now small enough to be in one file. * Removed the service from program main and use lazy singleton. * used OnInitialized * Refactoring and detecting login status in login.razor * Refactoring login to redirect if user is already logged in * Blazor login with MVC (#420) * Blazor login with MVC * return back the PasswordSignInAsync in Login page * CRUD added (#422) * CRUD added * Unit Test changed to meet new redirect /admin * CreateCatalogItemRequest added. * Action caption added. * Validation added for name and price. * Updated port of api Redirect to returnUrl from login * Add username to /admin; link to my profile * Working on authorization of /admin * Working on custom auth locking down /admin page * Microsoft authorize working.Login.razor removed.Login from SignInMana… (#425) * Microsoft authorize working.Login.razor removed.Login from SignInManager and create token from it.unit test fixed. * GetTokenFromController function used in CustomAuthStateProvider * Cleaned up button styles Refactored to use codebehind for List component Updated Not Authorized view Co-authored-by: Shady Nagy <shadynagi@gmail.com>
This commit is contained in:
220
src/BlazorAdmin/Services/AuthService.cs
Normal file
220
src/BlazorAdmin/Services/AuthService.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BlazorAdmin.JavaScript;
|
||||
using Blazored.LocalStorage;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.JSInterop;
|
||||
using Newtonsoft.Json;
|
||||
using Shared.Authorization;
|
||||
|
||||
namespace BlazorAdmin.Services
|
||||
{
|
||||
public class AuthService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILocalStorageService _localStorage;
|
||||
public bool IsLoggedIn { get; set; }
|
||||
public string UserName { get; set; }
|
||||
|
||||
public AuthService(HttpClient httpClient, ILocalStorageService localStorage)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_localStorage = localStorage;
|
||||
}
|
||||
|
||||
public HttpClient GetHttpClient()
|
||||
{
|
||||
return _httpClient;
|
||||
}
|
||||
|
||||
public async Task<AuthResponse> LoginWithoutSaveToLocalStorage(AuthRequest user)
|
||||
{
|
||||
var jsonContent = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PostAsync($"{Constants.API_URL}authenticate", jsonContent);
|
||||
var authResponse = new AuthResponse();
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
authResponse = await DeserializeToAuthResponse(response);
|
||||
|
||||
IsLoggedIn = true;
|
||||
}
|
||||
|
||||
return authResponse;
|
||||
}
|
||||
|
||||
public async Task<AuthResponse> Login(AuthRequest user)
|
||||
{
|
||||
var jsonContent = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PostAsync($"{Constants.API_URL}authenticate", jsonContent);
|
||||
var authResponse = new AuthResponse();
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
authResponse = await DeserializeToAuthResponse(response);
|
||||
await SaveTokenInLocalStorage(authResponse);
|
||||
await SaveUsernameInLocalStorage(authResponse);
|
||||
await SetAuthorizationHeader();
|
||||
|
||||
UserName = await GetUsername();
|
||||
IsLoggedIn = true;
|
||||
}
|
||||
|
||||
return authResponse;
|
||||
}
|
||||
|
||||
public async Task Logout()
|
||||
{
|
||||
await _localStorage.RemoveItemAsync("authToken");
|
||||
await _localStorage.RemoveItemAsync("username");
|
||||
|
||||
RemoveAuthorizationHeader();
|
||||
UserName = null;
|
||||
IsLoggedIn = false;
|
||||
}
|
||||
|
||||
public async Task RefreshLoginInfo()
|
||||
{
|
||||
await SetLoginData();
|
||||
}
|
||||
|
||||
public async Task RefreshLoginInfoFromCookie(IJSRuntime jSRuntime)
|
||||
{
|
||||
var token = await new Cookies(jSRuntime).GetCookie("token");
|
||||
await SaveTokenInLocalStorage(token);
|
||||
|
||||
var username = await new Cookies(jSRuntime).GetCookie("username");
|
||||
await SaveUsernameInLocalStorage(username);
|
||||
|
||||
await RefreshLoginInfo();
|
||||
}
|
||||
|
||||
private async Task SetLoginData()
|
||||
{
|
||||
IsLoggedIn = !string.IsNullOrEmpty(await GetToken());
|
||||
UserName = await GetUsername();
|
||||
await SetAuthorizationHeader();
|
||||
}
|
||||
|
||||
private async Task<AuthResponse> DeserializeToAuthResponse(HttpResponseMessage response)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<AuthResponse>(responseContent);
|
||||
}
|
||||
|
||||
private async Task SaveTokenInLocalStorage(AuthResponse authResponse)
|
||||
{
|
||||
await _localStorage.SetItemAsync("authToken", SaveTokenInLocalStorage(authResponse.Token));
|
||||
}
|
||||
|
||||
private async Task SaveTokenInLocalStorage(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _localStorage.SetItemAsync("authToken", token);
|
||||
}
|
||||
|
||||
private void RemoveAuthorizationHeader()
|
||||
{
|
||||
if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Remove("Authorization");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveUsernameInLocalStorage(AuthResponse authResponse)
|
||||
{
|
||||
await _localStorage.SetItemAsync("username", SaveUsernameInLocalStorage(authResponse.Username));
|
||||
}
|
||||
|
||||
private async Task SaveUsernameInLocalStorage(string username)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _localStorage.SetItemAsync("username", username);
|
||||
}
|
||||
|
||||
public async Task<string> GetToken()
|
||||
{
|
||||
|
||||
var token = await _localStorage.GetItemAsync<string>("authToken");
|
||||
return token;
|
||||
}
|
||||
|
||||
public async Task<UserInfo> GetTokenFromController()
|
||||
{
|
||||
return await _httpClient.GetFromJsonAsync<UserInfo>("User");
|
||||
}
|
||||
|
||||
public async Task<string> GetUsername()
|
||||
{
|
||||
var username = await _localStorage.GetItemAsync<string>("username");
|
||||
return username;
|
||||
}
|
||||
|
||||
private async Task SetAuthorizationHeader()
|
||||
{
|
||||
var token = await GetToken();
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
}
|
||||
|
||||
public IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
|
||||
{
|
||||
var claims = new List<Claim>();
|
||||
if (string.IsNullOrEmpty(jwt))
|
||||
{
|
||||
return claims;
|
||||
}
|
||||
|
||||
var payload = jwt.Split('.')[1];
|
||||
var jsonBytes = ParseBase64WithoutPadding(payload);
|
||||
var keyValuePairs = JsonConvert.DeserializeObject<Dictionary<string, object>>(Encoding.UTF8.GetString(jsonBytes));
|
||||
|
||||
keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);
|
||||
|
||||
if (roles != null)
|
||||
{
|
||||
if (roles.ToString().Trim().StartsWith("["))
|
||||
{
|
||||
var parsedRoles = JsonConvert.DeserializeObject<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;
|
||||
}
|
||||
|
||||
private byte[] ParseBase64WithoutPadding(string base64)
|
||||
{
|
||||
switch (base64.Length % 4)
|
||||
{
|
||||
case 2: base64 += "=="; break;
|
||||
case 3: base64 += "="; break;
|
||||
}
|
||||
return Convert.FromBase64String(base64);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user