23

I am using Asp.Net Core 2.1 Web Api with Swashbuckle.aspnetcore.swagger

I want to secure api documentation page with username and password before granting access.

Sample documention page enter image description here

To make sure its not accessible by the public

Joshua Leigh
  • 340
  • 1
  • 3
  • 8
  • 1
    Possible duplicate of [How to secure generated API documentation using swagger swashbuckle](https://stackoverflow.com/questions/39207264/how-to-secure-generated-api-documentation-using-swagger-swashbuckle) – Helder Sepulveda Jul 15 '18 at 23:18

2 Answers2

19

I have found a solution on GitHub and applied it to my project. its working as expected.

Below code copied from https://github.com/domaindrivendev/Swashbuckle.WebApi/issues/384#issuecomment-410117400

public class SwaggerBasicAuthMiddleware
{
    
private readonly RequestDelegate next;

    public SwaggerBasicAuthMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        //Make sure we are hitting the swagger path, and not doing it locally as it just gets annoying :-)
        if (context.Request.Path.StartsWithSegments("/swagger") && !this.IsLocalRequest(context))
        {
            string authHeader = context.Request.Headers["Authorization"];
            if (authHeader != null && authHeader.StartsWith("Basic "))
            {
                // Get the encoded username and password
                var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();

                // Decode from Base64 to string
                var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));

                // Split username and password
                var username = decodedUsernamePassword.Split(':', 2)[0];
                var password = decodedUsernamePassword.Split(':', 2)[1];

                // Check if login is correct
                if (IsAuthorized(username, password))
                {
                    await next.Invoke(context);
                    return;
                }
            }

            // Return authentication type (causes browser to show login dialog)
            context.Response.Headers["WWW-Authenticate"] = "Basic";

            // Return unauthorized
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        }
        else
        {
            await next.Invoke(context);
        }
    }

    public bool IsAuthorized(string username, string password)
    {
        // Check that username and password are correct
        return username.Equals("SpecialUser", StringComparison.InvariantCultureIgnoreCase)
                && password.Equals("SpecialPassword1");
    }

    public bool IsLocalRequest(HttpContext context)
    {
        //Handle running using the Microsoft.AspNetCore.TestHost and the site being run entirely locally in memory without an actual TCP/IP connection
        if (context.Connection.RemoteIpAddress == null && context.Connection.LocalIpAddress == null)
        {
            return true;
        }
        if (context.Connection.RemoteIpAddress.Equals(context.Connection.LocalIpAddress))
        {
            return true;
        }
        if (IPAddress.IsLoopback(context.Connection.RemoteIpAddress))
        {
            return true;
        }
        return false;
    }
}
public static class SwaggerAuthorizeExtensions
    {
        public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<SwaggerBasicAuthMiddleware>();
        }
    }

In Startup.cs

app.UseAuthentication(); //Ensure this like is above the swagger stuff

app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUI();
Matt
  • 659
  • 6
  • 11
Gautam Sharma
  • 851
  • 8
  • 13
  • 3
    A link to a solution is welcome, but please ensure your answer is useful without it: [add context around the link](//meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. [Answers that are little more than a link may be deleted.](//stackoverflow.com/help/deleted-answers) – double-beep Apr 11 '19 at 16:42
  • Worked flawless! Thank you very much! ^^ – Keenora Fluffball Dec 08 '21 at 16:25
17

Copied from mguinness's answer on Github:


In .NET Core you use middleware, instead of a DelegatingHandler:

public class SwaggerAuthorizedMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Path.StartsWithSegments("/swagger")
            && !context.User.Identity.IsAuthenticated)
        {
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            return;
        }

        await _next.Invoke(context);
    }
}

You will also need an extension method to help adding to pipeline:

public static class SwaggerAuthorizeExtensions
{
    public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<SwaggerAuthorizedMiddleware>();
    }
}

Then add to Configure method in Startup.cs just before using Swagger:

app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUi();

There's also a variant solution posted there how to do it with basic auth.

Shaul Behr
  • 36,951
  • 69
  • 249
  • 387
  • Any idea why this wouldn't redirect back to my login page like controller actions do? – Scott Wilson Oct 19 '21 at 19:10
  • Note: It's important to have UseAuthentication UseAuthorization before UserSwaggerAuthorized, otherwise the Identity won't be populated on the context. – John Feb 22 '23 at 09:01
  • @John I have configured it that way but when I hit middleware I get var isauth = context.User.Identity.IsAuthenticated set to false, You know why I can have something like it? more details here https://stackoverflow.com/questions/75794255/why-user-identity-isauthenticated-is-always-false-only-in-imiddleware – ruddnisrus Mar 20 '23 at 21:12