Implemented modular oauth2 system

This commit is contained in:
2025-04-15 13:03:13 +02:00
parent f81b84e4b3
commit 504cb8e950
7 changed files with 108 additions and 61 deletions

View File

@@ -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

View File

@@ -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<AuthController> Logger;
private readonly DatabaseRepository<User> 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<AuthController> logger,
DatabaseRepository<User> userRepository
DatabaseRepository<User> 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<LoginCompleteResponse> 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<OAuth2HandleResponse>("oauth2/handle", new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", request.Code),
new KeyValuePair<string, string>("redirect_uri", RedirectUri),
new KeyValuePair<string, string>("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<string, object>()

View File

@@ -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<LocalOAuth2Provider> Logger;
private readonly DatabaseRepository<User> UserRepository;
public LocalOAuth2Provider(
AppConfiguration configuration,
ILogger<LocalOAuth2Provider> logger,
DatabaseRepository<User> userRepository
)
{
UserRepository = userRepository;
Configuration = configuration;
Logger = logger;
}
public async Task<User?> 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<OAuth2HandleResponse>(accessEndpoint, new FormUrlEncodedContent(
[
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("redirect_uri", redirectUri),
new KeyValuePair<string, string>("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;
}
}

View File

@@ -0,0 +1,8 @@
using Moonlight.ApiServer.Database.Entities;
namespace Moonlight.ApiServer.Interfaces;
public interface IOAuth2Provider
{
public Task<User?> Sync(string code);
}

View File

@@ -1,8 +0,0 @@
using Moonlight.ApiServer.Database.Entities;
namespace Moonlight.ApiServer.Interfaces.OAuth2;
public interface IOAuth2Provider
{
public Task<User?> Sync(IServiceProvider provider, string accessToken);
}

View File

@@ -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)

View File

@@ -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;
@@ -587,6 +589,10 @@ public class Startup
WebApplicationBuilder.Services.AddAuthorization();
// Add local oauth2 provider if enabled
if (Configuration.Authentication.EnableLocalOAuth2)
WebApplicationBuilder.Services.AddScoped<IOAuth2Provider, LocalOAuth2Provider>();
return Task.CompletedTask;
}