From f9c4ec1d3122b965779c85735290c9b1257eb016 Mon Sep 17 00:00:00 2001 From: Masu Baumgartner <68913099+Masu-Baumgartner@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:03:18 +0100 Subject: [PATCH] Completed adjustments for the new configuration of oauth2 and token auth --- .../Implementations/TestyOuth2Provider.cs | 65 ------------- Moonlight.ApiServer/Program.cs | 2 - Moonlight.ApiServer/Startup.cs | 97 +++++++++++-------- 3 files changed, 54 insertions(+), 110 deletions(-) delete mode 100644 Moonlight.ApiServer/Implementations/TestyOuth2Provider.cs diff --git a/Moonlight.ApiServer/Implementations/TestyOuth2Provider.cs b/Moonlight.ApiServer/Implementations/TestyOuth2Provider.cs deleted file mode 100644 index 32053fc1..00000000 --- a/Moonlight.ApiServer/Implementations/TestyOuth2Provider.cs +++ /dev/null @@ -1,65 +0,0 @@ -using MoonCore.Extended.Abstractions; -using MoonCore.Extended.Interfaces; -using MoonCore.Extended.OAuth2.Models; -using MoonCore.Extensions; -using Moonlight.ApiServer.Configuration; -using Moonlight.ApiServer.Database.Entities; -using Moonlight.Shared.Http.Responses.OAuth2; - -namespace Moonlight.ApiServer.Implementations; - -public class TestyOuth2Provider : IOAuth2Provider -{ - public async Task> Sync(IServiceProvider provider, AccessData accessData) - { - var logger = provider.GetRequiredService>(); - - try - { - var configuration = provider.GetRequiredService(); - - using var httpClient = new HttpClient(); - - httpClient.DefaultRequestHeaders.Add("Authorization", accessData.AccessToken); - - var response = await httpClient.GetAsync($"{configuration.PublicUrl}/oauth2/info"); - await response.HandlePossibleApiError(); - var info = await response.ParseAsJson(); - - var userRepo = provider.GetRequiredService>(); - var user = userRepo.Get().FirstOrDefault(x => x.Email == info.Email); - - if (user == null) // User not found, register a new one - { - user = userRepo.Add(new User() - { - Email = info.Email, - Username = info.Username - }); - } - else if (user.Username != info.Username) // Username updated? - { - // Username not used by another user? - if (!userRepo.Get().Any(x => x.Username == info.Username)) - { - // Update username - user.Username = info.Username; - userRepo.Update(user); - } - } - - return new() - { - { - "userId", - user.Id - } - }; - } - catch (Exception e) - { - logger.LogCritical("Unable to sync user: {e}", e); - return new(); - } - } -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Program.cs b/Moonlight.ApiServer/Program.cs index a185c0a5..1718b76a 100644 --- a/Moonlight.ApiServer/Program.cs +++ b/Moonlight.ApiServer/Program.cs @@ -127,8 +127,6 @@ builder.Services.AddPlugins(configuration => configuration.AddAssembly(Assembly.GetEntryAssembly()!); }); -builder.Services.AddSingleton(new TestyOuth2Provider()); - var app = builder.Build(); await Startup.PrepareDatabase(app); diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs index b01ec915..4cc307fd 100644 --- a/Moonlight.ApiServer/Startup.cs +++ b/Moonlight.ApiServer/Startup.cs @@ -4,6 +4,7 @@ using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; using MoonCore.Extended.Extensions; using MoonCore.Extended.Helpers; +using MoonCore.Extended.OAuth2.Consumer; using MoonCore.Extensions; using MoonCore.Helpers; using Moonlight.ApiServer.Configuration; @@ -11,6 +12,7 @@ using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Helpers; using Moonlight.ApiServer.Interfaces.OAuth2; using Moonlight.ApiServer.Interfaces.Startup; +using Moonlight.Shared.Http.Responses.OAuth2; namespace Moonlight.ApiServer; @@ -127,7 +129,7 @@ public static class Startup return Task.FromResult(true); }; - authenticationConfig.ProcessRefresh = (oldData, newData, serviceProvider) => + authenticationConfig.ProcessRefresh = async (oldData, newData, serviceProvider) => { var oauth2Providers = serviceProvider.GetRequiredService(); @@ -141,7 +143,7 @@ public static class Startup if (!oldData.TryGetValue("userId", out var userIdStr) || !userIdStr.TryGetInt32(out var userId)) { - return Task.FromResult(false); + return false; } // Load user from database if existent @@ -152,24 +154,24 @@ public static class Startup .FirstOrDefault(x => x.Id == userId); if (user == null) - return Task.FromResult(false); + return false; // Allow plugins to intercept the refresh call //if (AuthInterceptors.Any(interceptor => !interceptor.AllowRefresh(user, serviceProvider))) // return false; - - /* // Check if it's time to resync with the oauth2 provider if (DateTime.UtcNow >= user.RefreshTimestamp) { + var oAuth2Service = serviceProvider.GetRequiredService(); + try { // It's time to refresh the access to the external oauth2 provider - var refreshData = OAuth2Service.RefreshAccess(user.RefreshToken).Result; + var refreshData = oAuth2Service.RefreshAccess(user.RefreshToken).Result; // Sync user with oauth2 provider - var syncedUser = provider.Sync(serviceProvider, refreshData.AccessToken).Result; + var syncedUser = await provider.Sync(serviceProvider, refreshData.AccessToken); if (syncedUser == null) // User sync has failed. No refresh allowed return false; @@ -178,7 +180,7 @@ public static class Startup // Fetch user model in current db context, just in case the oauth2 provider // uses a different db context or smth - var userModel = UserRepository + var userModel = userRepo .Get() .First(x => x.Id == syncedUser.Id); @@ -186,19 +188,22 @@ public static class Startup userModel.RefreshToken = refreshData.RefreshToken; userModel.RefreshTimestamp = DateTime.UtcNow.AddSeconds(refreshData.ExpiresIn); - UserRepository.Update(userModel); + userRepo.Update(userModel); } catch (Exception e) { + var loggerFactory = serviceProvider.GetRequiredService(); + var logger = loggerFactory.CreateLogger("OAuth2 Refresh"); + // We are handling this error more softly, because it will occur when a user hasn't logged in a long period of time - Logger.LogDebug("An error occured while refreshing external oauth2 access: {e}", e); + logger.LogDebug("An error occured while refreshing external oauth2 access: {e}", e); return false; } - }*/ + } // All checks have passed, allow refresh newData.Add("userId", user.Id); - return Task.FromResult(true); + return true; }; }); @@ -259,7 +264,7 @@ public static class Startup configuration.ClientId = config.Authentication.OAuth2.ClientId; configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; configuration.AuthorizationRedirect = - config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth"; + config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/"; configuration.AccessEndpoint = config.Authentication.OAuth2.AccessEndpoint ?? $"{config.PublicUrl}/oauth2/access"; @@ -279,41 +284,46 @@ public static class Startup configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationUri!; } + + // TODO: Make modular + configuration.ProcessComplete = async (serviceProvider, accessData) => + { + var loggerFactory = serviceProvider.GetRequiredService(); + var logger = loggerFactory.CreateLogger("OAuth2 Handler"); + + var oauth2Providers = serviceProvider.GetRequiredService(); + + // Find oauth2 provider + var provider = oauth2Providers.FirstOrDefault(); + + if (provider == null) + throw new HttpApiException("No oauth2 provider has been registered", 500); + + try + { + var user = await provider.Sync(serviceProvider, accessData.AccessToken); + + if (user == null) + throw new HttpApiException("OAuth2 provider returned empty user", 500); + + return new Dictionary() + { + {"userId", user.Id} + }; + } + catch (Exception e) + { + logger.LogTrace("An error occured while syncing user with oauth2 provider: {e}", e); + throw new HttpApiException("Unable to synchronize with oauth2 provider", 400); + } + }; }); - - /* - builder.Services.AddOAuth2Consumer(configuration => - { - configuration.ClientId = config.Authentication.OAuth2.ClientId; - configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; - configuration.AuthorizationRedirect = - 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"; - - if (config.Authentication.UseLocalOAuth2) - { - configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationRedirect ?? - $"{config.PublicUrl}/oauth2/authorize"; - } - else - { - if (config.Authentication.OAuth2.AuthorizationUri == null) - logger.LogWarning( - "The 'AuthorizationUri' for the oauth2 client is not set. If you want to use an external oauth2 provider, you need to specify this url. If you want to use the local oauth2 service, set 'UseLocalOAuth2Service' to true"); - - configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationUri!; - } - });*/ if (!config.Authentication.UseLocalOAuth2) return Task.CompletedTask; logger.LogInformation("Using local oauth2 provider"); - builder.Services.AddOAuth2Provider(configuration => + builder.AddOAuth2Provider(configuration => { configuration.AccessSecret = config.Authentication.LocalOAuth2.AccessSecret; configuration.RefreshSecret = config.Authentication.LocalOAuth2.RefreshSecret; @@ -322,7 +332,7 @@ public static class Startup configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; configuration.CodeSecret = config.Authentication.LocalOAuth2.CodeSecret; configuration.AuthorizationRedirect = - config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth"; + config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/"; configuration.AccessTokenDuration = 60; configuration.RefreshTokenDuration = 3600; }); @@ -333,6 +343,7 @@ public static class Startup public static Task UseOAuth2(WebApplication application) { application.UseOAuth2Consumer(); + return Task.CompletedTask; }