Implemented better ux for the oauth2 workflow

Still wip
This commit is contained in:
Masu Baumgartner
2024-10-20 01:05:46 +02:00
parent f166de1a43
commit c4c3d1bd60
7 changed files with 178 additions and 13 deletions

View File

@@ -13,6 +13,7 @@ using MoonCore.Models;
using MoonCore.PluginFramework.Services;
using Moonlight.Client.Implementations;
using Moonlight.Client.Interfaces;
using Moonlight.Client.Services;
using Moonlight.Client.UI;
using Moonlight.Shared.Http.Requests.Auth;
@@ -89,6 +90,7 @@ builder.Services.AddScoped(sp =>
});
builder.Services.AddMoonCoreBlazorTailwind();
builder.Services.AddScoped<WindowService>();
builder.Services.AutoAddServices<Program>();

View File

@@ -0,0 +1,19 @@
using Microsoft.JSInterop;
namespace Moonlight.Client.Services;
public class WindowService
{
private readonly IJSRuntime JsRuntime;
public WindowService(IJSRuntime jsRuntime)
{
JsRuntime = jsRuntime;
}
public async Task Open(string url, string title, int height, int width)
=> await JsRuntime.InvokeVoidAsync("moonlight.window.open", url, title, height, width);
public async Task Close()
=> await JsRuntime.InvokeVoidAsync("moonlight.window.closeCurrent");
}

View File

@@ -1,16 +1,77 @@
@page "/auth"
@using MoonCore.Helpers
@using Moonlight.Client.Services
@using Moonlight.Shared.Http.Requests.Auth
@using Moonlight.Shared.Http.Responses.Auth
@inject NavigationManager Navigation
@inject HttpApiClient HttpApiClient
@inject CookieService CookieService
@inject WindowService WindowService
@inject HttpClient HttpClient
<div class="flex justify-center">
<WButton OnClick="StartAuth" CssClasses="btn btn-primary">Authenticate</WButton>
</div>
@if (Code == null)
{
<div class="h-full w-full min-h-[100dvh] flex items-center justify-center px-5 md:px-0">
<div class="card card-body w-full max-w-lg">
@if (IsAuthenticating)
{
<div class="sm:mx-auto sm:w-full sm:max-w-sm mb-5">
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-100">Login flow started in new window/tab</h2>
</div>
<div class="flex justify-center">
<div id="loader" class="my-10"></div>
</div>
}
else
{
<div class="sm:mx-auto sm:w-full sm:max-w-sm mb-5">
<img class="mx-auto h-16 w-auto" src="/logolong.webp" alt="Moonlight">
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-100">Login to access your account</h2>
</div>
<div class="flex justify-center">
<WButton OnClick="StartAuth" CssClasses="btn btn-primary">
Proceed to login
<i class="bi bi-arrow-right"></i>
</WButton>
</div>
}
</div>
</div>
}
else
{
<div class="h-full w-full min-h-[100dvh] flex items-center justify-center px-5 md:px-0">
<div class="card card-body w-full max-w-lg">
@if (IsHandlingDone)
{
<div class="sm:mx-auto sm:w-full sm:max-w-sm mb-5">
<img class="mx-auto h-16 w-auto" src="/logolong.webp" alt="Moonlight">
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-100">Login to access your account</h2>
</div>
<div class="flex justify-center">
<p class="mt-3 text-center text-md text-gray-400">
Login successful. You can close this window now
</p>
</div>
}
else
{
<div class="flex justify-center">
<div id="loader"></div>
</div>
}
</div>
</div>
}
@code
{
@@ -18,12 +79,15 @@
[Parameter]
public string? Code { get; set; }
private bool IsAuthenticating = false;
private bool IsHandlingDone = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(!firstRender)
if (!firstRender)
return;
if(Code == null)
if (Code == null)
return;
var authHandleData = await HttpApiClient.PostJson<OAuth2HandleResponse>("api/auth", new OAuth2HandleRequest()
@@ -34,6 +98,16 @@
await CookieService.SetValue("kms-access", authHandleData.AccessToken, 10);
await CookieService.SetValue("kms-refresh", authHandleData.RefreshToken, 10);
await CookieService.SetValue("kms-timestamp", DateTimeOffset.UtcNow.AddSeconds(60).ToUnixTimeSeconds().ToString(), 10);
try
{
await WindowService.Close();
}
finally
{
IsHandlingDone = true;
await InvokeAsync(StateHasChanged);
}
}
private async Task StartAuth(WButton _)
@@ -44,7 +118,46 @@
+ $"?client_id={authStartData.ClientId}" +
$"&redirect_uri={authStartData.RedirectUri}" +
$"&response_type=code";
Navigation.NavigateTo(uri, true);
try
{
await WindowService.Open(
uri,
"OAuth2 Flow",
500,
650
);
IsAuthenticating = true;
await InvokeAsync(StateHasChanged);
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
try
{
if (HttpClient.DefaultRequestHeaders.Contains("Authorization"))
HttpClient.DefaultRequestHeaders.Remove("Authorization");
HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + await CookieService.GetValue("kms-access", "x"));
var res = await HttpClient.GetAsync("api/auth/check");
if(res.IsSuccessStatusCode)
break;
}
finally{}
}
Navigation.NavigateTo(Navigation.Uri, true);
});
}
catch (Exception)
{
Navigation.NavigateTo(uri, true);
}
}
}
}

View File

@@ -27,6 +27,7 @@
<a class="dismiss">🗙</a>
</div>
<script src="/js/moonlight.js"></script>
<script src="/_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
</body>

View File

@@ -0,0 +1,30 @@
window.moonlight = {
window: {
open: function (url, title, w, h) {
// Fixes dual-screen position Most browsers Firefox
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX;
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY;
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
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();
},
closeCurrent() {
window.close();
}
}
};