From 5a215231fadae43d1e11f133770615bc479d91b8 Mon Sep 17 00:00:00 2001 From: ChiaraBm Date: Thu, 24 Jul 2025 09:23:36 +0200 Subject: [PATCH] Cleaned up pagination in user and apikey controller. Extracted login start and start url generation to modular IOAuth2Provider interface. Improved login and register local oauth2 page --- .../Admin/ApiKeys/ApiKeysController.cs | 4 +- .../Admin/Users/UsersController.cs | 2 +- .../Http/Controllers/Auth/AuthController.cs | 36 +++++---------- .../Controllers/Frontend/FrontendPage.razor | 10 +++-- .../Implementations/LocalOAuth2Provider.cs | 26 ++++++++++- .../Interfaces/IOAuth2Provider.cs | 4 +- .../Services/FrontendService.cs | 45 ++++++++----------- .../Services/RemoteAuthStateManager.cs | 7 +-- .../Http/Responses/Auth/LoginStartResponse.cs | 4 +- .../Misc/FrontendConfiguration.cs | 10 ----- 10 files changed, 67 insertions(+), 81 deletions(-) diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs index 370ed26e..c77f303e 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs @@ -28,8 +28,8 @@ public class ApiKeysController : Controller [HttpGet] [Authorize(Policy = "permissions:admin.apikeys.get")] public async Task> Get( - [FromQuery] int page, - [FromQuery] [Range(1, 100)] int pageSize = 50 + [FromQuery] [Range(0, int.MaxValue)] int page, + [FromQuery] [Range(1, 100)] int pageSize ) { var count = await ApiKeyRepository.Get().CountAsync(); diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs index 59b42c05..ce3c6d3e 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs @@ -27,7 +27,7 @@ public class UsersController : Controller [Authorize(Policy = "permissions:admin.users.get")] public async Task> Get( [FromQuery] [Range(0, int.MaxValue)] int page, - [FromQuery] [Range(1, 100)] int pageSize = 50 + [FromQuery] [Range(1, 100)] int pageSize ) { var count = await UserRepository.Get().CountAsync(); diff --git a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs index aee4328d..ddef78fb 100644 --- a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs @@ -1,9 +1,9 @@ using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; @@ -20,16 +20,11 @@ namespace Moonlight.ApiServer.Http.Controllers.Auth; public class AuthController : Controller { private readonly AppConfiguration Configuration; - private readonly ILogger Logger; private readonly DatabaseRepository UserRepository; private readonly IOAuth2Provider OAuth2Provider; - private readonly string RedirectUri; - private readonly string EndpointUri; - public AuthController( AppConfiguration configuration, - ILogger logger, DatabaseRepository userRepository, IOAuth2Provider oAuth2Provider ) @@ -37,36 +32,25 @@ public class AuthController : Controller UserRepository = userRepository; OAuth2Provider = oAuth2Provider; Configuration = configuration; - Logger = logger; - - RedirectUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationRedirect) - ? Configuration.PublicUrl - : Configuration.Authentication.OAuth2.AuthorizationRedirect; - - EndpointUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationEndpoint) - ? Configuration.PublicUrl + "/oauth2/authorize" - : Configuration.Authentication.OAuth2.AuthorizationEndpoint; } [AllowAnonymous] [HttpGet("start")] - public Task Start() + public async Task Start() { - var response = new LoginStartResponse() - { - ClientId = Configuration.Authentication.OAuth2.ClientId, - RedirectUri = RedirectUri, - Endpoint = EndpointUri - }; + var url = await OAuth2Provider.Start(); - return Task.FromResult(response); + return new LoginStartResponse() + { + Url = url + }; } [AllowAnonymous] [HttpPost("complete")] public async Task Complete([FromBody] LoginCompleteRequest request) { - var user = await OAuth2Provider.Sync(request.Code); + var user = await OAuth2Provider.Complete(request.Code); if (user == null) throw new HttpApiException("Unable to load user data", 500); @@ -113,8 +97,8 @@ public class AuthController : Controller [HttpGet("check")] public async Task Check() { - var userIdClaim = User.Claims.First(x => x.Type == "userId"); - var userId = int.Parse(userIdClaim.Value); + var userIdStr = User.FindFirstValue("userId")!; + var userId = int.Parse(userIdStr); var user = await UserRepository.Get().FirstAsync(x => x.Id == userId); return new() diff --git a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor index 4e97033a..d7a7efca 100644 --- a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor +++ b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendPage.razor @@ -7,10 +7,10 @@ - @Configuration.Title + @Title - @foreach (var style in Configuration.Styles) + @foreach (var style in Styles) { } @@ -86,7 +86,7 @@ -@foreach (var script in Configuration.Scripts) +@foreach (var script in Scripts) { } @@ -99,6 +99,8 @@ @code { - [Parameter] public FrontendConfiguration Configuration { get; set; } + [Parameter] public string Title { get; set; } + [Parameter] public string[] Scripts { get; set; } + [Parameter] public string[] Styles { get; set; } [Parameter] public Theme? Theme { get; set; } } diff --git a/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs b/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs index 3586e74a..e763ff65 100644 --- a/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs +++ b/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs @@ -27,7 +27,27 @@ public class LocalOAuth2Provider : IOAuth2Provider Logger = logger; } - public async Task Sync(string code) + public Task Start() + { + var redirectUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationRedirect) + ? Configuration.PublicUrl + : Configuration.Authentication.OAuth2.AuthorizationRedirect; + + var endpoint = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationEndpoint) + ? Configuration.PublicUrl + "/oauth2/authorize" + : Configuration.Authentication.OAuth2.AuthorizationEndpoint; + + var clientId = Configuration.Authentication.OAuth2.ClientId; + + var url = $"{endpoint}" + + $"?client_id={clientId}" + + $"&redirect_uri={redirectUri}" + + $"&response_type=code"; + + return Task.FromResult(url); + } + + public async Task Complete(string code) { // Create http client to call the auth provider var httpClient = new HttpClient(); @@ -70,6 +90,10 @@ public class LocalOAuth2Provider : IOAuth2Provider throw new HttpApiException("Unable to request user data", 500); } + // Notice: We just look up the user id here + // which works as our oauth2 provider is using the same db. + // a real oauth2 provider would create a user here + // Handle the returned data var userId = handleData.UserId; diff --git a/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs b/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs index b28cdd30..3d9d19c5 100644 --- a/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs +++ b/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs @@ -4,5 +4,7 @@ namespace Moonlight.ApiServer.Interfaces; public interface IOAuth2Provider { - public Task Sync(string code); + public Task Start(); + + public Task Complete(string code); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Services/FrontendService.cs b/Moonlight.ApiServer/Services/FrontendService.cs index f00e677e..bfd72b97 100644 --- a/Moonlight.ApiServer/Services/FrontendService.cs +++ b/Moonlight.ApiServer/Services/FrontendService.cs @@ -40,53 +40,44 @@ public class FrontendService ThemeRepository = themeRepository; } - public async Task GetConfiguration() + public Task GetConfiguration() { var configuration = new FrontendConfiguration() { - Title = "Moonlight", // TODO: CONFIG ApiUrl = Configuration.PublicUrl, HostEnvironment = "ApiServer" }; - // Load theme.json if it exists - var themePath = Path.Combine("storage", "theme.json"); - - if (File.Exists(themePath)) - { - var variablesJson = await File.ReadAllTextAsync(themePath); - - configuration.Theme.Variables = JsonSerializer - .Deserialize>(variablesJson) ?? new(); - } - - // Collect scripts to execute - configuration.Scripts = ConfigurationOptions - .SelectMany(x => x.Scripts) - .ToArray(); - - // Collect styles - configuration.Styles = ConfigurationOptions - .SelectMany(x => x.Styles) - .ToArray(); - - return configuration; + return Task.FromResult(configuration); } public async Task GenerateIndexHtml() // TODO: Cache { - var configuration = await GetConfiguration(); - + // Load requested theme var theme = await ThemeRepository .Get() .FirstOrDefaultAsync(x => x.IsEnabled); + + // Load configured javascript files + var scripts = ConfigurationOptions + .SelectMany(x => x.Scripts) + .Distinct() + .ToArray(); + + // Load configured css files + var styles = ConfigurationOptions + .SelectMany(x => x.Styles) + .Distinct() + .ToArray(); return await ComponentHelper.RenderComponent( ServiceProvider, parameters => { - parameters["Configuration"] = configuration; parameters["Theme"] = theme!; + parameters["Styles"] = styles; + parameters["Scripts"] = scripts; + parameters["Title"] = "Moonlight"; // TODO: Config } ); } diff --git a/Moonlight.Client/Services/RemoteAuthStateManager.cs b/Moonlight.Client/Services/RemoteAuthStateManager.cs index caae24cb..6af53153 100644 --- a/Moonlight.Client/Services/RemoteAuthStateManager.cs +++ b/Moonlight.Client/Services/RemoteAuthStateManager.cs @@ -83,12 +83,7 @@ public class RemoteAuthStateManager : AuthenticationStateManager { var loginStartData = await HttpApiClient.GetJson("api/auth/start"); - var url = $"{loginStartData.Endpoint}" + - $"?client_id={loginStartData.ClientId}" + - $"&redirect_uri={loginStartData.RedirectUri}" + - $"&response_type=code"; - - NavigationManager.NavigateTo(url, true); + NavigationManager.NavigateTo(loginStartData.Url, true); } private async Task LoadAuthState() diff --git a/Moonlight.Shared/Http/Responses/Auth/LoginStartResponse.cs b/Moonlight.Shared/Http/Responses/Auth/LoginStartResponse.cs index 8c23813b..2130beef 100644 --- a/Moonlight.Shared/Http/Responses/Auth/LoginStartResponse.cs +++ b/Moonlight.Shared/Http/Responses/Auth/LoginStartResponse.cs @@ -2,7 +2,5 @@ namespace Moonlight.Shared.Http.Responses.Auth; public class LoginStartResponse { - public string ClientId { get; set; } - public string Endpoint { get; set; } - public string RedirectUri { get; set; } + public string Url { get; set; } } \ No newline at end of file diff --git a/Moonlight.Shared/Misc/FrontendConfiguration.cs b/Moonlight.Shared/Misc/FrontendConfiguration.cs index 7a2befdd..cedbdab4 100644 --- a/Moonlight.Shared/Misc/FrontendConfiguration.cs +++ b/Moonlight.Shared/Misc/FrontendConfiguration.cs @@ -2,16 +2,6 @@ namespace Moonlight.Shared.Misc; public class FrontendConfiguration { - public string Title { get; set; } public string ApiUrl { get; set; } public string HostEnvironment { get; set; } - public ThemeData Theme { get; set; } = new(); - public string[] Scripts { get; set; } - public string[] Styles { get; set; } - public string[] Assemblies { get; set; } - - public class ThemeData - { - public Dictionary Variables { get; set; } = new(); - } } \ No newline at end of file