diff --git a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs index 035ade57..aa2b4a6c 100644 --- a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs @@ -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; diff --git a/Moonlight.ApiServer/Program.cs b/Moonlight.ApiServer/Program.cs index 5ff3c734..6f3cf15a 100644 --- a/Moonlight.ApiServer/Program.cs +++ b/Moonlight.ApiServer/Program.cs @@ -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; }); diff --git a/Moonlight.Client/Program.cs b/Moonlight.Client/Program.cs index f1a6224e..a9c4685f 100644 --- a/Moonlight.Client/Program.cs +++ b/Moonlight.Client/Program.cs @@ -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(); builder.Services.AutoAddServices(); diff --git a/Moonlight.Client/Services/WindowService.cs b/Moonlight.Client/Services/WindowService.cs new file mode 100644 index 00000000..266b4b9d --- /dev/null +++ b/Moonlight.Client/Services/WindowService.cs @@ -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"); +} \ No newline at end of file diff --git a/Moonlight.Client/UI/Screens/AuthenticationScreen.razor b/Moonlight.Client/UI/Screens/AuthenticationScreen.razor index f2f61668..fec1a05f 100644 --- a/Moonlight.Client/UI/Screens/AuthenticationScreen.razor +++ b/Moonlight.Client/UI/Screens/AuthenticationScreen.razor @@ -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 -
- Authenticate -
+@if (Code == null) +{ +
+ +
+ @if (IsAuthenticating) + { +
+

Login flow started in new window/tab

+
+ +
+
+
+ } + else + { +
+ Moonlight +

Login to access your account

+
+ +
+ + Proceed to login + + +
+ } +
+ +
+} +else +{ +
+ +
+ @if (IsHandlingDone) + { +
+ Moonlight +

Login to access your account

+
+ +
+

+ Login successful. You can close this window now +

+
+ } + else + { +
+
+
+ } +
+ +
+} @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("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); + } } -} +} \ No newline at end of file diff --git a/Moonlight.Client/wwwroot/index.html b/Moonlight.Client/wwwroot/index.html index 21ef0215..b5710305 100644 --- a/Moonlight.Client/wwwroot/index.html +++ b/Moonlight.Client/wwwroot/index.html @@ -27,6 +27,7 @@ 🗙 + diff --git a/Moonlight.Client/wwwroot/js/moonlight.js b/Moonlight.Client/wwwroot/js/moonlight.js new file mode 100644 index 00000000..051581ee --- /dev/null +++ b/Moonlight.Client/wwwroot/js/moonlight.js @@ -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(); + } + } +}; \ No newline at end of file