2

I have added Swagger and Swashbuckle generator to my site following this tutorial. Now, when navigating to https://localhost:port/swagger/ I can see the generated API documentation. Note, that I have not created any SwaggerController class - this is all handled by the NuGet package.

The problem is, my whole site, even the API, is authenticated using custom LDAP. I would like to protect the /swagger/ page as well. However, I did not find a way how to do that. The only related question on StackOverflow describes adding authentication INTO swagger requests - not authenticating the whole API documentation page.

Is there a specific way how to protect the generated /swagger/ page? Or, is there a general way of adding authentication validators to ASP.NET Core 2.0 MVC routes?

lss
  • 1,235
  • 3
  • 15
  • 24

2 Answers2

6

Create a custom middleware handler and then add it to the pipeline like below:

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAuthorization();

            app.UseMvc();
            app.UseStaticFiles();

            //And here's where the middleware is registered
            app.UseRequestAuthHandler();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            });
        }

Middleware Class:

namespace SwaggerDemo.Handlers
{
    using System.Net;
    using System.Threading.Tasks;

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Http;

    public class RequestAuthHandler
    {
        private const string _swaggerPathIdentifier = "swagger";
        private readonly RequestDelegate _next;

        public RequestAuthHandler(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // First check if the current path is the swagger path
            if (context.Request.Path.HasValue && context.Request.Path.Value.ToLower().Contains(_swaggerPathIdentifier))
            {
                // Secondly check if the current user is authenticated
                if (!context.User.Identity.IsAuthenticated)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    return;
                }
            }
            await _next.Invoke(context);
        }
    }

    public static class RequestAuthHandlerExtension
    {
        public static IApplicationBuilder UseRequestAuthHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestAuthHandler>();
        }
    }
}
Ryan Gunn
  • 1,161
  • 7
  • 18
1

I came up with the following solution: (Inspired from Ryan's solution)

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using System;

/// <summary>
/// The extension methods that extends <see cref="IApplicationBuilder" /> for authentication purposes
/// </summary>
public static class ApplicationBuilderExtensions
{
    /// <summary>
    /// Requires authentication for paths that starts with <paramref name="pathPrefix" />
    /// </summary>
    /// <param name="app">The application builder</param>
    /// <param name="pathPrefix">The path prefix</param>
    /// <returns>The application builder</returns>
    public static IApplicationBuilder RequireAuthenticationOn(this IApplicationBuilder app, string pathPrefix)
    {
        return app.Use((context, next) =>
        {
            // First check if the current path is the swagger path
            if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith(pathPrefix, StringComparison.InvariantCultureIgnoreCase))
            {
                // Secondly check if the current user is authenticated
                if (!context.User.Identity.IsAuthenticated)
                {
                    return context.ChallengeAsync();
                }
            }

            return next();
        });
    }
}

This will redirect the user to the login page if you have properly set up the authentication mecanism.

Then, when building your app (for example for NSwag)

app.RequireAuthenticationOn("/swagger");
//Enable Swagger + Swagger Ui
app.UseSwaggerUi3WithApiExplorer(this.ConfigureSwagger);
cube45
  • 3,429
  • 2
  • 24
  • 35