Switched to LocalStorage. Upgraded MoonCore. Improved auth flow

This commit is contained in:
Masu Baumgartner
2024-10-23 21:37:26 +02:00
parent 910f190c86
commit 6f3341e6ad
7 changed files with 76 additions and 89 deletions

View File

@@ -12,13 +12,14 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MoonCore" Version="1.6.4" /> <PackageReference Include="MoonCore" Version="1.6.6" />
<PackageReference Include="MoonCore.Blazor" Version="1.2.4" />
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.7" />
<PackageReference Include="MoonCore.Extended" Version="1.1.2" /> <PackageReference Include="MoonCore.Extended" Version="1.1.2" />
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" /> <PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
<PackageReference Include="Ben.Demystifier" Version="0.4.1" /> <PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.6" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -10,10 +10,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6"/> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" PrivateAssets="all"/> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" PrivateAssets="all"/>
<PackageReference Include="MoonCore" Version="1.6.4" /> <PackageReference Include="MoonCore" Version="1.6.6" />
<PackageReference Include="MoonCore.Blazor" Version="1.2.1" /> <PackageReference Include="MoonCore.Blazor" Version="1.2.4" />
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" /> <PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.6" /> <PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.7" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,12 +1,11 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MoonCore.Blazor.Extensions;
using MoonCore.Blazor.Services;
using MoonCore.Blazor.Tailwind.Extensions; using MoonCore.Blazor.Tailwind.Extensions;
using MoonCore.Blazor.Tailwind.Forms; using MoonCore.Blazor.Tailwind.Forms;
using MoonCore.Blazor.Tailwind.Forms.Components; using MoonCore.Blazor.Tailwind.Forms.Components;
using MoonCore.Blazor.Tailwind.Services; using MoonCore.Exceptions;
using MoonCore.Extensions; using MoonCore.Extensions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Models; using MoonCore.Models;
@@ -55,42 +54,41 @@ builder.Logging.AddProviders(providers);
builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after"); builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddScoped(_ => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped(sp => builder.Services.AddScoped(sp =>
{ {
var httpClient = sp.GetRequiredService<HttpClient>(); var httpClient = sp.GetRequiredService<HttpClient>();
var localStorageService = sp.GetRequiredService<LocalStorageService>();
var result = new HttpApiClient(httpClient); var result = new HttpApiClient(httpClient);
result.UseBearerTokenConsumer(async () => result.AddLocalStorageTokenAuthentication(localStorageService, async refreshToken =>
{ {
var cookieService = sp.GetRequiredService<CookieService>(); try
{
var httpApiClient = new HttpApiClient(httpClient);
return new TokenConsumer( var response = await httpApiClient.PostJson<RefreshResponse>(
await cookieService.GetValue("kms-access", "x"), "api/auth/refresh",
await cookieService.GetValue("kms-refresh", "x"), new RefreshRequest()
DateTimeOffset.FromUnixTimeSeconds(long.Parse(await cookieService.GetValue("kms-timestamp", "0"))).UtcDateTime,
async refreshToken =>
{
var response = await httpClient.PostAsync("api/auth/refresh", new StringContent(
JsonSerializer.Serialize(new RefreshRequest()
{ {
RefreshToken = refreshToken RefreshToken = refreshToken
}), new MediaTypeHeaderValue("application/json")
));
var refreshRes = await response.ParseAsJson<RefreshResponse>();
await cookieService.SetValue("kms-access", refreshRes.AccessToken, 10);
await cookieService.SetValue("kms-refresh", refreshRes.RefreshToken, 10);
await cookieService.SetValue("kms-timestamp", DateTimeOffset.UtcNow.AddSeconds(60).ToUnixTimeSeconds().ToString(), 10);
return new TokenPair()
{
AccessToken = await cookieService.GetValue("kms-access", "x"),
RefreshToken = await cookieService.GetValue("kms-refresh", "x")
};
} }
); );
return (new TokenPair()
{
AccessToken = response.AccessToken,
RefreshToken = response.RefreshToken
}, response.ExpiresAt);
}
catch (HttpApiException)
{
return (new TokenPair()
{
AccessToken = "unset",
RefreshToken = "unset"
}, DateTime.MinValue);
}
}); });
return result; return result;
@@ -98,6 +96,7 @@ builder.Services.AddScoped(sp =>
builder.Services.AddMoonCoreBlazorTailwind(); builder.Services.AddMoonCoreBlazorTailwind();
builder.Services.AddScoped<WindowService>(); builder.Services.AddScoped<WindowService>();
builder.Services.AddScoped<LocalStorageService>();
builder.Services.AutoAddServices<Program>(); builder.Services.AutoAddServices<Program>();

View File

@@ -1,7 +1,10 @@
using MoonCore.Attributes; using MoonCore.Attributes;
using MoonCore.Blazor.Services;
using MoonCore.Blazor.Tailwind.Services; using MoonCore.Blazor.Tailwind.Services;
using MoonCore.Exceptions; using MoonCore.Exceptions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Models;
using Moonlight.Shared.Http.Requests.Auth;
using Moonlight.Shared.Http.Responses.Auth; using Moonlight.Shared.Http.Responses.Auth;
namespace Moonlight.Client.Services; namespace Moonlight.Client.Services;
@@ -14,24 +17,20 @@ public class IdentityService
public string[] Permissions { get; private set; } public string[] Permissions { get; private set; }
public bool IsLoggedIn { get; private set; } public bool IsLoggedIn { get; private set; }
public event Func<Task> OnStateChanged; private readonly HttpApiClient HttpApiClient;
private readonly LocalStorageService LocalStorageService;
private readonly CookieService CookieService; public IdentityService(HttpApiClient httpApiClient, LocalStorageService localStorageService)
private readonly HttpApiClient ApiClient;
public IdentityService(CookieService cookieService, HttpApiClient apiClient)
{ {
CookieService = cookieService; HttpApiClient = httpApiClient;
ApiClient = apiClient; LocalStorageService = localStorageService;
} }
#region Login / Logout
public async Task Check() public async Task Check()
{ {
try try
{ {
var response = await ApiClient.GetJson<CheckResponse>("api/auth/check"); var response = await HttpApiClient.GetJson<CheckResponse>("api/auth/check");
Username = response.Username; Username = response.Username;
Email = response.Email; Email = response.Email;
@@ -47,20 +46,13 @@ public class IdentityService
//await OnStateChanged?.Invoke(); //await OnStateChanged?.Invoke();
} }
public async Task Login(string token)
{
await CookieService.SetValue("token", token, 30);
await Check();
}
public async Task Logout() public async Task Logout()
{ {
await CookieService.SetValue("token", "", 30); await LocalStorageService.SetString("AccessToken", "unset");
await Check(); await LocalStorageService.SetString("RefreshToken", "unset");
await LocalStorageService.Set("ExpiresAt", DateTime.MinValue);
} }
#endregion
public bool HasPermission(string requiredPermission) public bool HasPermission(string requiredPermission)
{ {
// Check for wildcard permission // Check for wildcard permission

View File

@@ -3,6 +3,7 @@
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ToastService ToastService @inject ToastService ToastService
@inject NavigationManager Navigation
<div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 bg-gray-800/60 backdrop-blur px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8"> <div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 bg-gray-800/60 backdrop-blur px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
@if (Layout.ShowMobileNavigation) @if (Layout.ShowMobileNavigation)
@@ -112,6 +113,7 @@
await IdentityService.Logout(); await IdentityService.Logout();
await ToastService.Info("Successfully logged out"); await ToastService.Info("Successfully logged out");
await Layout.Load(); //await Layout.Load();
Navigation.NavigateTo(Navigation.Uri, true);
} }
} }

View File

@@ -1,5 +1,6 @@
@page "/auth" @page "/auth"
@using MoonCore.Blazor.Services
@using MoonCore.Helpers @using MoonCore.Helpers
@using Moonlight.Client.Services @using Moonlight.Client.Services
@using Moonlight.Shared.Http.Requests.Auth @using Moonlight.Shared.Http.Requests.Auth
@@ -7,9 +8,9 @@
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject HttpApiClient HttpApiClient @inject HttpApiClient HttpApiClient
@inject CookieService CookieService
@inject WindowService WindowService @inject WindowService WindowService
@inject HttpClient HttpClient @inject HttpClient HttpClient
@inject LocalStorageService LocalStorageService
@if (Code == null) @if (Code == null)
{ {
@@ -95,9 +96,9 @@ else
Code = Code Code = Code
}); });
await CookieService.SetValue("kms-access", authHandleData.AccessToken, 10); await LocalStorageService.SetString("AccessToken", authHandleData.AccessToken);
await CookieService.SetValue("kms-refresh", authHandleData.RefreshToken, 10); await LocalStorageService.SetString("RefreshToken", authHandleData.RefreshToken);
await CookieService.SetValue("kms-timestamp", DateTimeOffset.UtcNow.AddSeconds(60).ToUnixTimeSeconds().ToString(), 10); await LocalStorageService.Set("ExpiresAt", authHandleData.ExpiresAt);
try try
{ {
@@ -124,8 +125,8 @@ else
await WindowService.Open( await WindowService.Open(
uri, uri,
"OAuth2 Flow", "OAuth2 Flow",
500, 600,
650 450
); );
IsAuthenticating = true; IsAuthenticating = true;
@@ -139,17 +140,23 @@ else
try try
{ {
if(!await LocalStorageService.ContainsKey("AccessToken"))
continue;
if (HttpClient.DefaultRequestHeaders.Contains("Authorization")) if (HttpClient.DefaultRequestHeaders.Contains("Authorization"))
HttpClient.DefaultRequestHeaders.Remove("Authorization"); HttpClient.DefaultRequestHeaders.Remove("Authorization");
HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + await CookieService.GetValue("kms-access", "x")); HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + await LocalStorageService.GetString("AccessToken"));
var res = await HttpClient.GetAsync("api/auth/check"); var res = await HttpClient.GetAsync("api/auth/check");
if(res.IsSuccessStatusCode) if (res.IsSuccessStatusCode)
break; break;
} }
finally{} catch (Exception e)
{
Console.WriteLine(e);
}
} }
Navigation.NavigateTo(Navigation.Uri, true); Navigation.NavigateTo(Navigation.Uri, true);

View File

@@ -1,25 +1,11 @@
window.moonlight = { window.moonlight = {
window: { window: {
open: function (url, title, w, h) { open: function (url, title, w, h) {
// Fixes dual-screen position Most browsers Firefox let height = w;
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX; let width = h;
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY; var left = (screen.width - width) / 2;
var top = (screen.height - height) / 2;
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; var newWindow = window.open(url, title, 'resizable = yes, width=' + width + ', height=' + height + ', top=' + top + ', left=' + left);
const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
const systemZoom = width / window.screen.availWidth;
const left = (width - w) / 2 / systemZoom + dualScreenLeft
const top = (height - h) / 2 / systemZoom + dualScreenTop
const newWindow = window.open(url, title,
`
scrollbars=yes,
width=${w / systemZoom},
height=${h / systemZoom},
top=${top},
left=${left}
`
)
if (window.focus) newWindow.focus(); if (window.focus) newWindow.focus();
}, },