Implemented modular oauth2 system
This commit is contained in:
@@ -20,7 +20,7 @@ public class AppConfiguration
|
|||||||
public class DatabaseConfig
|
public class DatabaseConfig
|
||||||
{
|
{
|
||||||
public string Host { get; set; } = "your-database-host.name";
|
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 Username { get; set; } = "db_user";
|
||||||
public string Password { get; set; } = "db_password";
|
public string Password { get; set; } = "db_password";
|
||||||
@@ -31,8 +31,9 @@ public class AppConfiguration
|
|||||||
public class AuthenticationConfig
|
public class AuthenticationConfig
|
||||||
{
|
{
|
||||||
public string Secret { get; set; } = Formatter.GenerateString(32);
|
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 OAuth2Data OAuth2 { get; set; } = new();
|
||||||
|
|
||||||
public class OAuth2Data
|
public class OAuth2Data
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using MoonCore.Extended.Abstractions;
|
|||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.ApiServer.Configuration;
|
using Moonlight.ApiServer.Configuration;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
using Moonlight.ApiServer.Interfaces;
|
||||||
using Moonlight.Shared.Http.Requests.Auth;
|
using Moonlight.Shared.Http.Requests.Auth;
|
||||||
using Moonlight.Shared.Http.Responses.Auth;
|
using Moonlight.Shared.Http.Responses.Auth;
|
||||||
using Moonlight.Shared.Http.Responses.OAuth2;
|
using Moonlight.Shared.Http.Responses.OAuth2;
|
||||||
@@ -23,6 +24,7 @@ public class AuthController : Controller
|
|||||||
private readonly AppConfiguration Configuration;
|
private readonly AppConfiguration Configuration;
|
||||||
private readonly ILogger<AuthController> Logger;
|
private readonly ILogger<AuthController> Logger;
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
private readonly DatabaseRepository<User> UserRepository;
|
||||||
|
private readonly IOAuth2Provider OAuth2Provider;
|
||||||
|
|
||||||
private readonly string RedirectUri;
|
private readonly string RedirectUri;
|
||||||
private readonly string EndpointUri;
|
private readonly string EndpointUri;
|
||||||
@@ -30,12 +32,14 @@ public class AuthController : Controller
|
|||||||
public AuthController(
|
public AuthController(
|
||||||
AppConfiguration configuration,
|
AppConfiguration configuration,
|
||||||
ILogger<AuthController> logger,
|
ILogger<AuthController> logger,
|
||||||
DatabaseRepository<User> userRepository
|
DatabaseRepository<User> userRepository,
|
||||||
|
IOAuth2Provider oAuth2Provider
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
UserRepository = userRepository;
|
||||||
|
OAuth2Provider = oAuth2Provider;
|
||||||
Configuration = configuration;
|
Configuration = configuration;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
UserRepository = userRepository;
|
|
||||||
|
|
||||||
RedirectUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationRedirect)
|
RedirectUri = string.IsNullOrEmpty(Configuration.Authentication.OAuth2.AuthorizationRedirect)
|
||||||
? Configuration.PublicUrl
|
? Configuration.PublicUrl
|
||||||
@@ -64,52 +68,7 @@ public class AuthController : Controller
|
|||||||
[HttpPost("complete")]
|
[HttpPost("complete")]
|
||||||
public async Task<LoginCompleteResponse> Complete([FromBody] LoginCompleteRequest request)
|
public async Task<LoginCompleteResponse> Complete([FromBody] LoginCompleteRequest request)
|
||||||
{
|
{
|
||||||
// TODO: Make modular
|
var user = await OAuth2Provider.Sync(request.Code);
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new HttpApiException("Unable to load user data", 500);
|
throw new HttpApiException("Unable to load user data", 500);
|
||||||
@@ -120,7 +79,7 @@ public class AuthController : Controller
|
|||||||
// Generate token
|
// Generate token
|
||||||
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
||||||
{
|
{
|
||||||
Expires = DateTime.Now.AddDays(10), // TODO: config
|
Expires = DateTime.Now.AddYears(Configuration.Authentication.TokenDuration),
|
||||||
IssuedAt = DateTime.Now,
|
IssuedAt = DateTime.Now,
|
||||||
NotBefore = DateTime.Now.AddMinutes(-1),
|
NotBefore = DateTime.Now.AddMinutes(-1),
|
||||||
Claims = new Dictionary<string, object>()
|
Claims = new Dictionary<string, object>()
|
||||||
|
|||||||
81
Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs
Normal file
81
Moonlight.ApiServer/Implementations/LocalOAuth2Provider.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs
Normal file
8
Moonlight.ApiServer/Interfaces/IOAuth2Provider.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
|
||||||
|
namespace Moonlight.ApiServer.Interfaces;
|
||||||
|
|
||||||
|
public interface IOAuth2Provider
|
||||||
|
{
|
||||||
|
public Task<User?> Sync(string code);
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -132,7 +132,7 @@ public class FrontendService
|
|||||||
await ArchiveFsItem(zipArchive, wwwRootPluginPath, wwwRootPluginPath);
|
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");
|
var assembliesMap = PluginService.GetAssemblies("client");
|
||||||
|
|
||||||
foreach (var assemblyName in assembliesMap.Keys)
|
foreach (var assemblyName in assembliesMap.Keys)
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ using Moonlight.ApiServer.Configuration;
|
|||||||
using Moonlight.ApiServer.Database;
|
using Moonlight.ApiServer.Database;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using Moonlight.ApiServer.Helpers;
|
using Moonlight.ApiServer.Helpers;
|
||||||
|
using Moonlight.ApiServer.Implementations;
|
||||||
|
using Moonlight.ApiServer.Interfaces;
|
||||||
using Moonlight.ApiServer.Interfaces.Startup;
|
using Moonlight.ApiServer.Interfaces.Startup;
|
||||||
using Moonlight.ApiServer.Models;
|
using Moonlight.ApiServer.Models;
|
||||||
using Moonlight.ApiServer.Services;
|
using Moonlight.ApiServer.Services;
|
||||||
@@ -586,6 +588,10 @@ public class Startup
|
|||||||
});
|
});
|
||||||
|
|
||||||
WebApplicationBuilder.Services.AddAuthorization();
|
WebApplicationBuilder.Services.AddAuthorization();
|
||||||
|
|
||||||
|
// Add local oauth2 provider if enabled
|
||||||
|
if (Configuration.Authentication.EnableLocalOAuth2)
|
||||||
|
WebApplicationBuilder.Services.AddScoped<IOAuth2Provider, LocalOAuth2Provider>();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user