diff --git a/Moonlight.ApiServer/Configuration/AppConfiguration.cs b/Moonlight.ApiServer/Configuration/AppConfiguration.cs index 54038ce0..4c5bd173 100644 --- a/Moonlight.ApiServer/Configuration/AppConfiguration.cs +++ b/Moonlight.ApiServer/Configuration/AppConfiguration.cs @@ -20,7 +20,7 @@ public class AppConfiguration public class DatabaseConfig { public string Host { get; set; } = "your-database-host.name"; - public int Port { get; set; } = 3306; + public int Port { get; set; } = 5432; public string Username { get; set; } = "db_user"; public string Password { get; set; } = "db_password"; @@ -31,8 +31,9 @@ public class AppConfiguration public class AuthenticationConfig { public string Secret { get; set; } = Formatter.GenerateString(32); - public int TokenDuration { get; set; } = 3600; - + public int TokenDuration { get; set; } = 24 * 10; + + public bool EnableLocalOAuth2 { get; set; } = true; public OAuth2Data OAuth2 { get; set; } = new(); public class OAuth2Data diff --git a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs index 50cd2be8..a705fc24 100644 --- a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs @@ -10,6 +10,7 @@ using MoonCore.Extended.Abstractions; using MoonCore.Helpers; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database.Entities; +using Moonlight.ApiServer.Interfaces; using Moonlight.Shared.Http.Requests.Auth; using Moonlight.Shared.Http.Responses.Auth; using Moonlight.Shared.Http.Responses.OAuth2; @@ -23,6 +24,7 @@ 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; @@ -30,12 +32,14 @@ public class AuthController : Controller public AuthController( AppConfiguration configuration, ILogger logger, - DatabaseRepository userRepository + DatabaseRepository userRepository, + IOAuth2Provider oAuth2Provider ) { + UserRepository = userRepository; + OAuth2Provider = oAuth2Provider; Configuration = configuration; Logger = logger; - UserRepository = userRepository; RedirectUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationRedirect) ? Configuration.PublicUrl @@ -64,52 +68,7 @@ public class AuthController : Controller [HttpPost("complete")] public async Task Complete([FromBody] LoginCompleteRequest request) { - // TODO: Make modular - - // Create http client to call the auth provider - using var httpClient = new HttpClient(); - - httpClient.BaseAddress = new Uri( - string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AccessEndpoint) - ? Configuration.PublicUrl - : Configuration.Authentication.OAuth2.AccessEndpoint - ); - - httpClient.DefaultRequestHeaders.Add("Authorization", - $"Basic {Configuration.Authentication.OAuth2.ClientSecret}"); - - var httpApiClient = new HttpApiClient(httpClient); - - // Call the auth provider - OAuth2HandleResponse handleData; - - try - { - handleData = await httpApiClient.PostJson("oauth2/handle", new FormUrlEncodedContent( - [ - new KeyValuePair("grant_type", "authorization_code"), - new KeyValuePair("code", request.Code), - new KeyValuePair("redirect_uri", RedirectUri), - new KeyValuePair("client_id", Configuration.Authentication.OAuth2.ClientId) - ] - )); - } - catch (HttpApiException e) - { - if (e.Status == 400) - Logger.LogTrace("The auth server returned an error: {e}", e); - else - Logger.LogCritical("The auth server returned an error: {e}", e); - - throw new HttpApiException("Unable to request user data", 500); - } - - // Handle the returned data - var userId = handleData.UserId; - - var user = await UserRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == userId); + var user = await OAuth2Provider.Sync(request.Code); if (user == null) throw new HttpApiException("Unable to load user data", 500); @@ -120,7 +79,7 @@ public class AuthController : Controller // Generate token var securityTokenDescriptor = new SecurityTokenDescriptor() { - Expires = DateTime.Now.AddDays(10), // TODO: config + Expires = DateTime.Now.AddYears(Configuration.Authentication.TokenDuration), IssuedAt = DateTime.Now, NotBefore = DateTime.Now.AddMinutes(-1), Claims = new Dictionary() diff --git a/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs b/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs new file mode 100644 index 00000000..98a12200 --- /dev/null +++ b/Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore; +using MoonCore.Exceptions; +using MoonCore.Extended.Abstractions; +using MoonCore.Helpers; +using Moonlight.ApiServer.Configuration; +using Moonlight.ApiServer.Database.Entities; +using Moonlight.ApiServer.Interfaces; +using Moonlight.Shared.Http.Responses.OAuth2; + +namespace Moonlight.ApiServer.Implementations; + +public class LocalOAuth2Provider : IOAuth2Provider +{ + private readonly AppConfiguration Configuration; + private readonly ILogger Logger; + private readonly DatabaseRepository UserRepository; + + public LocalOAuth2Provider( + AppConfiguration configuration, + ILogger logger, + DatabaseRepository userRepository + ) + { + UserRepository = userRepository; + Configuration = configuration; + Logger = logger; + } + + public async Task Sync(string code) + { + // Create http client to call the auth provider + var httpClient = new HttpClient(); + using var httpApiClient = new HttpApiClient(httpClient); + + httpClient.DefaultRequestHeaders.Add("Authorization", + $"Basic {Configuration.Authentication.OAuth2.ClientSecret}"); + + // Build access endpoint + var accessEndpoint = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AccessEndpoint) + ? $"{Configuration.PublicUrl}/oauth2/handle" + : Configuration.Authentication.OAuth2.AccessEndpoint; + + // Build redirect uri + var redirectUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationRedirect) + ? Configuration.PublicUrl + : Configuration.Authentication.OAuth2.AuthorizationRedirect; + + // Call the auth provider + OAuth2HandleResponse handleData; + + try + { + handleData = await httpApiClient.PostJson(accessEndpoint, new FormUrlEncodedContent( + [ + new KeyValuePair("grant_type", "authorization_code"), + new KeyValuePair("code", code), + new KeyValuePair("redirect_uri", redirectUri), + new KeyValuePair("client_id", Configuration.Authentication.OAuth2.ClientId) + ] + )); + } + catch (HttpApiException e) + { + if (e.Status == 400) + Logger.LogTrace("The auth server returned an error: {e}", e); + else + Logger.LogCritical("The auth server returned an error: {e}", e); + + throw new HttpApiException("Unable to request user data", 500); + } + + // Handle the returned data + var userId = handleData.UserId; + + var user = await UserRepository + .Get() + .FirstOrDefaultAsync(x => x.Id == userId); + + return user; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs b/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs new file mode 100644 index 00000000..b28cdd30 --- /dev/null +++ b/Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs @@ -0,0 +1,8 @@ +using Moonlight.ApiServer.Database.Entities; + +namespace Moonlight.ApiServer.Interfaces; + +public interface IOAuth2Provider +{ + public Task Sync(string code); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/OAuth2/IOAuth2Provider.cs b/Moonlight.ApiServer/Interfaces/OAuth2/IOAuth2Provider.cs deleted file mode 100644 index e63ef23e..00000000 --- a/Moonlight.ApiServer/Interfaces/OAuth2/IOAuth2Provider.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Moonlight.ApiServer.Database.Entities; - -namespace Moonlight.ApiServer.Interfaces.OAuth2; - -public interface IOAuth2Provider -{ - public Task Sync(IServiceProvider provider, string accessToken); -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Services/FrontendService.cs b/Moonlight.ApiServer/Services/FrontendService.cs index 8c2fc3be..936d6f2b 100644 --- a/Moonlight.ApiServer/Services/FrontendService.cs +++ b/Moonlight.ApiServer/Services/FrontendService.cs @@ -132,7 +132,7 @@ public class FrontendService await ArchiveFsItem(zipArchive, wwwRootPluginPath, wwwRootPluginPath); } - // Add plugin assemblies (TODO: Test this thing) + // Add plugin assemblies for client to the zip file var assembliesMap = PluginService.GetAssemblies("client"); foreach (var assemblyName in assembliesMap.Keys) diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs index d97f713e..b14e1491 100644 --- a/Moonlight.ApiServer/Startup.cs +++ b/Moonlight.ApiServer/Startup.cs @@ -20,6 +20,8 @@ using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database; using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Helpers; +using Moonlight.ApiServer.Implementations; +using Moonlight.ApiServer.Interfaces; using Moonlight.ApiServer.Interfaces.Startup; using Moonlight.ApiServer.Models; using Moonlight.ApiServer.Services; @@ -586,6 +588,10 @@ public class Startup }); WebApplicationBuilder.Services.AddAuthorization(); + + // Add local oauth2 provider if enabled + if (Configuration.Authentication.EnableLocalOAuth2) + WebApplicationBuilder.Services.AddScoped(); return Task.CompletedTask; }