Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Secure swagger docs page with username and password Asp Net Core 2.1

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

like image 287
Joshua Leigh Avatar asked Jul 15 '18 20:07

Joshua Leigh


2 Answers

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();
like image 108
Gautam Sharma Avatar answered Nov 11 '22 02:11

Gautam Sharma


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.

like image 29
Shaul Behr Avatar answered Nov 11 '22 04:11

Shaul Behr