Search code examples
asp.net-core-mvc.net-8.0nswag

Apply authorization filter to Swagger UI endpoint (NSwag)


I have an ASP.NET Core 8.0 MVC application to which I'm adding NSwag.

The web application uses a custom authorization filter to apply custom authorization logic to most of the MVC controllers (but not all of them), and I'd like to also apply the logic for this attribute to the /swagger endpoint registered by NSwag, so that only authorized users are able to view the Swagger UI.

How can I do this?

Swagger UI is enabled simply in Startup.Configure() using:

app.UseSwaggerUi();

The custom authorization filter is set up as follows:

public sealed class MyAuthorizeFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // Do custom authorization logic here and set context.Result accordingly.
        // ...
    }
}

// This attribute is applied to certain MVC controllers and methods, which require authorization.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class MyAuthorizeAttribute : TypeFilterAttribute
{
    public MyAuthorizeAttribute(params string[] arguments)
        : base(typeof(MyAuthorizeFilter)
    {
        // ...
    }
}

Example usage:

[MyAuthorize]
public class ExampleController : Controller { ... }

Solution

  • You can create a custom middleware to apply the MyAuthorizeFilter logic on the /swagger endpoint.

    Custom Middleware

    public class CustomSwaggerAuthorizationMiddleware
    {
        private readonly RequestDelegate _next;
        public CustomSwaggerAuthorizationMiddleware(RequestDelegate next)
        {
            _next = next;    
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            // Here, you can manually invoke the custom authorization filter
            var authorizationFilterContext = new AuthorizationFilterContext(
                new ActionContext(context, new RouteData(), new ActionDescriptor()),
                new List<IFilterMetadata>()
            );
    
            var filter = new MyAuthorizeFilter();
    
            filter.OnAuthorization(authorizationFilterContext);
    
            if (authorizationFilterContext.Result is UnauthorizedResult)
            {
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                return;
            }
    
            // If authorized, continue processing the request.
            await _next(context);
        }
    }
    

    Your Authorization filter should be like below:

    public sealed class MyAuthorizeFilter : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            // Custom authorization logic to check user access
            var isAuthorized = /* your custom logic to check authorization */;
    
            if (!isAuthorized)
            {
                context.Result = new UnauthorizedResult();
            }
        }
    }
    

    Then add the middleware before Swagger

    app.UseWhen(context => context.Request.Path.StartsWithSegments("/swagger"), swaggerApp =>
     {
         swaggerApp.UseMiddleware<CustomSwaggerAuthorizationMiddleware>();
     });
     app.UseOpenApi();
     app.UseSwaggerUi();