Added login/register function. Implemented authentication. Started authorization

This commit is contained in:
Masu-Baumgartner
2024-10-01 11:29:19 +02:00
parent 73bf27d222
commit ef2e6c9a20
23 changed files with 741 additions and 27 deletions

View File

@@ -1,15 +1,107 @@
namespace Moonlight.ApiServer.Http.Middleware;
using System.Text.Json;
using MoonCore.Extended.Abstractions;
using MoonCore.Extended.Helpers;
using MoonCore.Services;
using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Database.Entities;
using Moonlight.ApiServer.Helpers.Authentication;
namespace Moonlight.ApiServer.Http.Middleware;
public class AuthenticationMiddleware
{
private readonly RequestDelegate Next;
private readonly ILogger<AuthenticationMiddleware> Logger;
public AuthenticationMiddleware(RequestDelegate next)
public AuthenticationMiddleware(RequestDelegate next, ILogger<AuthenticationMiddleware> logger)
{
Next = next;
Logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
await Authenticate(context);
await Next(context);
}
private async Task Authenticate(HttpContext context)
{
var request = context.Request;
string? token = null;
// Cookie for Moonlight.Client
if (request.Cookies.ContainsKey("token") && !string.IsNullOrEmpty(request.Cookies["token"]))
token = request.Cookies["token"];
// Header for api clients
if (request.Headers.ContainsKey("Authorization") && !string.IsNullOrEmpty(request.Cookies["Authorization"]))
{
var headerValue = request.Cookies["Authorization"] ?? "";
if (headerValue.StartsWith("Bearer"))
{
var headerParts = headerValue.Split(" ");
if (headerParts.Length > 1 && !string.IsNullOrEmpty(headerParts[1]))
token = headerParts[1];
}
}
if(token == null)
return;
// Validate token
if (token.Length > 300)
{
Logger.LogWarning("Received token bigger than 300 characters, Length: {length}", token.Length);
return;
}
// Decide which authentication method we need for the token
if (token.Count(x => x == '.') == 2) // JWT only has two dots
await AuthenticateUser(context, token);
else
await AuthenticateApiKey(context, token);
}
private async Task AuthenticateUser(HttpContext context, string jwt)
{
var jwtHelper = context.RequestServices.GetRequiredService<JwtHelper>();
var configService = context.RequestServices.GetRequiredService<ConfigService<AppConfiguration>>();
var secret = configService.Get().Authentication.Secret;
if(!await jwtHelper.Validate(secret, jwt, "login"))
return;
var data = await jwtHelper.Decode(secret, jwt);
if(!data.TryGetValue("iat", out var issuedAtString) || !data.TryGetValue("userId", out var userIdString))
return;
var userId = int.Parse(userIdString);
var issuedAt = DateTimeOffset.FromUnixTimeSeconds(long.Parse(issuedAtString)).DateTime;
var userRepo = context.RequestServices.GetRequiredService<DatabaseRepository<User>>();
var user = userRepo.Get().FirstOrDefault(x => x.Id == userId);
if(user == null)
return;
// Check if token is in the past
if(user.TokenValidTimestamp > issuedAt)
return;
// Load permissions, handle empty values
var permissions = JsonSerializer.Deserialize<string[]>(
string.IsNullOrEmpty(user.PermissionsJson) ? "[]" : user.PermissionsJson
) ?? [];
// Save permission state
context.User = new PermClaimsPrinciple(permissions, user);
}
private async Task AuthenticateApiKey(HttpContext context, string apiKey)
{
}

View File

@@ -0,0 +1,76 @@
using Microsoft.AspNetCore.Mvc.Controllers;
using Moonlight.ApiServer.Attributes;
namespace Moonlight.ApiServer.Http.Middleware;
public class AuthorisationMiddleware
{
private readonly RequestDelegate Next;
private readonly ILogger<AuthorisationMiddleware> Logger;
public AuthorisationMiddleware(RequestDelegate next, ILogger<AuthorisationMiddleware> logger)
{
Next = next;
Logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
await Next(context);
}
private async Task Authorize(HttpContext context)
{
}
private string[] ResolveRequiredPermissions(HttpContext context)
{
// Basic handling
var endpoint = context.GetEndpoint();
if (endpoint == null)
return [];
var metadata = endpoint
.Metadata
.GetMetadata<ControllerActionDescriptor>();
if (metadata == null)
return [];
// Retrieve attribute infos
var controllerAttrInfo = metadata
.ControllerTypeInfo
.CustomAttributes
.FirstOrDefault(x => x.AttributeType == typeof(RequirePermissionAttribute));
var methodAttrInfo = metadata
.MethodInfo
.CustomAttributes
.FirstOrDefault(x => x.AttributeType == typeof(RequirePermissionAttribute));
// Retrieve permissions from attribute infos
var controllerPermission = controllerAttrInfo != null
? controllerAttrInfo.ConstructorArguments.First().Value as string
: null;
var methodPermission = methodAttrInfo != null
? methodAttrInfo.ConstructorArguments.First().Value as string
: null;
// If both have a permission flag, return both
if (controllerPermission != null && methodPermission != null)
return [controllerPermission, methodPermission];
// If either of them have a permission set, return it
if (controllerPermission != null)
return [controllerPermission];
if (methodPermission != null)
return [methodPermission];
// If both have no permission set, allow everyone to access it
return [];
}
}