Added authentication

This commit is contained in:
2026-06-21 20:10:02 +02:00
parent efe63211ed
commit a50be1ec0b
50 changed files with 2596 additions and 328 deletions
+65 -2
View File
@@ -2,11 +2,15 @@
using System.Xml.Linq;
using EonaCat.DoxaApi.Attributes;
using EonaCat.DoxaApi.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace EonaCat.DoxaApi.Generation
{
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/license for full license details.
public sealed class ApiDocumentGenerator
{
private readonly IReadOnlyList<ActionDescriptor> _actions;
@@ -80,6 +84,12 @@ namespace EonaCat.DoxaApi.Generation
}
doc.Schemas = schemaRegistry;
doc.SecuritySchemes = _options.SecuritySchemes;
doc.Features = new ApiFeatureFlags
{
MockServer = _options.EnableMockServer,
Console = _options.EnableConsole
};
return doc;
}
@@ -112,6 +122,52 @@ namespace EonaCat.DoxaApi.Generation
return name;
}
/// <summary>
/// Determines which security schemes apply to an action, in priority order:
/// 1. Explicit [DoxaApiAllowAnonymous] on method or class -> no security.
/// 2. Explicit [DoxaApiAuth("schemeId")] on method (falls back to class) -> those schemes.
/// 3. Standard ASP.NET Core [Authorize] on method or class, combined with
/// DoxaApiOptions.DefaultSecurityScheme if one is configured.
/// 4. Otherwise: anonymous.
/// </summary>
private List<string> ResolveSecurity(ControllerActionDescriptor cad)
{
var method = cad.MethodInfo;
var controller = cad.ControllerTypeInfo;
bool anonymous = method.GetCustomAttribute<DoxaApiAllowAnonymousAttribute>() is not null
|| controller.GetCustomAttribute<DoxaApiAllowAnonymousAttribute>() is not null
|| method.GetCustomAttribute<AllowAnonymousAttribute>() is not null;
if (anonymous)
{
return new List<string>();
}
var methodSchemes = method.GetCustomAttributes<DoxaApiAuthAttribute>().Select(a => a.SchemeId).ToList();
if (methodSchemes.Count > 0)
{
return methodSchemes.Where(_options.SecuritySchemes.ContainsKey).ToList();
}
var classSchemes = controller.GetCustomAttributes<DoxaApiAuthAttribute>().Select(a => a.SchemeId).ToList();
if (classSchemes.Count > 0)
{
return classSchemes.Where(_options.SecuritySchemes.ContainsKey).ToList();
}
bool requiresAuth = method.GetCustomAttribute<AuthorizeAttribute>() is not null
|| controller.GetCustomAttribute<AuthorizeAttribute>() is not null;
if (requiresAuth && _options.DefaultSecurityScheme is not null
&& _options.SecuritySchemes.ContainsKey(_options.DefaultSecurityScheme))
{
return new List<string> { _options.DefaultSecurityScheme };
}
return new List<string>();
}
private ApiEndpoint? BuildEndpoint(ControllerActionDescriptor cad, SchemaBuilder schemaBuilder)
{
var httpMethod = cad.ActionConstraints?
@@ -136,7 +192,8 @@ namespace EonaCat.DoxaApi.Generation
Summary = summaryAttr?.Summary ?? xmlDoc?.Summary ?? HumanizeName(cad.ActionName),
Description = descAttr?.Description ?? xmlDoc?.Remarks,
Deprecated = obsoleteAttr is not null,
Tags = new List<string> { ResolveGroupName(cad) }
Tags = new List<string> { ResolveGroupName(cad) },
Security = ResolveSecurity(cad)
};
foreach (var param in cad.Parameters)
@@ -207,6 +264,12 @@ namespace EonaCat.DoxaApi.Generation
endpoint.Responses.Add(new ResponseModel { StatusCode = "400", Description = "Invalid request" });
}
if (endpoint.Security.Count > 0)
{
endpoint.Responses.Add(new ResponseModel { StatusCode = "401", Description = "Unauthorized - missing or invalid credentials" });
endpoint.Responses.Add(new ResponseModel { StatusCode = "403", Description = "Forbidden - insufficient permissions" });
}
return endpoint;
}
@@ -275,4 +338,4 @@ namespace EonaCat.DoxaApi.Generation
return reader;
}
}
}
}