Split authentication and authorization

This commit is contained in:
Laura Hausmann 2024-01-28 00:32:33 +01:00
parent e755f9f96f
commit 64a70e688c
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
6 changed files with 28 additions and 19 deletions

View file

@ -1,4 +1,3 @@
using System.Net;
using System.Net.Mime;
using Iceshrimp.Backend.Controllers.Schemas;
using Iceshrimp.Backend.Core.Database;
@ -19,16 +18,15 @@ namespace Iceshrimp.Backend.Controllers;
[Route("/api/iceshrimp/v1/auth")]
public class AuthController(DatabaseContext db, UserService userSvc) : Controller {
[HttpGet]
[Authentication(false)]
[Authenticate]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(AuthResponse))]
public IActionResult GetAuthStatus() {
var session = Request.HttpContext.GetSession();
if (session == null) {
if (session == null)
return Ok(new AuthResponse {
Status = AuthStatusEnum.Guest
});
}
return Ok(new AuthResponse {
Status = session.Active ? AuthStatusEnum.Authenticated : AuthStatusEnum.TwoFactor,

View file

@ -39,6 +39,7 @@ public static class ServiceExtensions {
services.AddSingleton<QueueService>();
services.AddSingleton<ErrorHandlerMiddleware>();
services.AddSingleton<RequestBufferingMiddleware>();
services.AddSingleton<AuthorizationMiddleware>();
// Hosted services = long running background tasks
// Note: These need to be added as a singleton as well to ensure data consistency

View file

@ -12,6 +12,7 @@ public static class WebApplicationExtensions {
return app.UseMiddleware<ErrorHandlerMiddleware>()
.UseMiddleware<RequestBufferingMiddleware>()
.UseMiddleware<AuthenticationMiddleware>()
.UseMiddleware<AuthorizationMiddleware>()
.UseMiddleware<AuthorizedFetchMiddleware>();
}

View file

@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Microsoft.AspNetCore.Http.Features;
@ -6,20 +5,15 @@ using Microsoft.EntityFrameworkCore;
namespace Iceshrimp.Backend.Core.Middleware;
public class AuthenticationMiddleware(
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")]
DatabaseContext db
) : IMiddleware {
public class AuthenticationMiddleware(DatabaseContext db) : IMiddleware {
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) {
var endpoint = ctx.Features.Get<IEndpointFeature>()?.Endpoint;
var attribute = endpoint?.Metadata.GetMetadata<AuthenticationAttribute>();
var attribute = endpoint?.Metadata.GetMetadata<AuthenticateAttribute>();
if (attribute != null) {
var request = ctx.Request;
var header = request.Headers.Authorization.ToString();
if (!header.ToLowerInvariant().StartsWith("bearer ")) {
if (attribute.Required)
throw GracefulException.Unauthorized("Missing bearer token in authorization header");
await next(ctx);
return;
}
@ -27,11 +21,10 @@ public class AuthenticationMiddleware(
var token = header[7..];
var session = await db.Sessions.Include(p => p.User).FirstOrDefaultAsync(p => p.Token == token);
if (session == null) {
if (attribute.Required)
throw GracefulException.Forbidden("Bearer token is invalid");
await next(ctx);
return;
}
ctx.SetSession(session);
}
@ -39,9 +32,7 @@ public class AuthenticationMiddleware(
}
}
public class AuthenticationAttribute(bool required = true) : Attribute {
public bool Required { get; } = required;
}
public class AuthenticateAttribute : Attribute;
public static class HttpContextExtensions {
private const string Key = "session";
@ -49,7 +40,7 @@ public static class HttpContextExtensions {
internal static void SetSession(this HttpContext ctx, Session session) {
ctx.Items.Add(Key, session);
}
public static Session? GetSession(this HttpContext ctx) {
ctx.Items.TryGetValue(Key, out var session);
return session as Session;

View file

@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Http.Features;
namespace Iceshrimp.Backend.Core.Middleware;
public class AuthorizationMiddleware : IMiddleware {
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) {
var endpoint = ctx.Features.Get<IEndpointFeature>()?.Endpoint;
var attribute = endpoint?.Metadata.GetMetadata<AuthorizeAttribute>();
if (attribute != null)
if (ctx.GetSession() is not { Active: true })
throw GracefulException.Forbidden("This method requires an authenticated user");
await next(ctx);
}
}
public class AuthorizeAttribute : Attribute;

View file

@ -77,7 +77,7 @@ public class GracefulException(HttpStatusCode statusCode, string error, string m
public static GracefulException Forbidden(string message, string? details = null) {
return new GracefulException(HttpStatusCode.Forbidden, message, details);
}
public static GracefulException Unauthorized(string message, string? details = null) {
return new GracefulException(HttpStatusCode.Unauthorized, message, details);
}