Implemented better ux for the oauth2 workflow
Still wip
This commit is contained in:
@@ -166,7 +166,7 @@ public class AuthController : Controller
|
||||
return false;
|
||||
|
||||
// Check if it's time to resync with the oauth2 provider
|
||||
if (DateTime.UtcNow >= user.RefreshTimestamp)
|
||||
if (false && DateTime.UtcNow >= user.RefreshTimestamp)
|
||||
{
|
||||
// It's time to refresh the access to the external oauth2 provider
|
||||
var refreshData = OAuth2Service.RefreshAccess(user.RefreshToken).Result;
|
||||
|
||||
@@ -75,7 +75,7 @@ var logConfigPath = PathBuilder.File("storage", "logConfig.json");
|
||||
if (!File.Exists(logConfigPath))
|
||||
{
|
||||
await File.WriteAllTextAsync(logConfigPath,
|
||||
"{\"LogLevel\":{\"Default\":\"Information\",\"Microsoft.AspNetCore\":\"Warning\"}}");
|
||||
"{\"LogLevel\":{\"Default\":\"Information\",\"Microsoft.AspNetCore\":\"Warning\",\"MoonCore.Extended.Helpers.JwtHelper\": \"Error\"}}");
|
||||
}
|
||||
|
||||
builder.Logging.AddConfiguration(await File.ReadAllTextAsync(logConfigPath));
|
||||
@@ -99,7 +99,7 @@ builder.Services.AddOAuth2Consumer(configuration =>
|
||||
configuration.ClientId = config.Authentication.OAuth2.ClientId;
|
||||
configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret;
|
||||
configuration.AuthorizationRedirect =
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/code";
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth";
|
||||
|
||||
configuration.AccessEndpoint = config.Authentication.OAuth2.AccessEndpoint ?? $"{config.PublicUrl}/oauth2/access";
|
||||
configuration.RefreshEndpoint = config.Authentication.OAuth2.RefreshEndpoint ?? $"{config.PublicUrl}/oauth2/refresh";
|
||||
@@ -130,7 +130,7 @@ if (config.Authentication.UseLocalOAuth2)
|
||||
configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret;
|
||||
configuration.CodeSecret = config.Authentication.LocalOAuth2.CodeSecret;
|
||||
configuration.AuthorizationRedirect =
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/code";
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth";
|
||||
configuration.AccessTokenDuration = 60;
|
||||
configuration.RefreshTokenDuration = 3600;
|
||||
});
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
19
Moonlight.Client/Services/WindowService.cs
Normal file
19
Moonlight.Client/Services/WindowService.cs
Normal 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");
|
||||
}
|
||||
@@ -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 _)
|
||||
@@ -45,6 +119,45 @@
|
||||
$"&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
30
Moonlight.Client/wwwroot/js/moonlight.js
Normal file
30
Moonlight.Client/wwwroot/js/moonlight.js
Normal 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user