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
To make sure its not accessible by the public
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
To make sure its not accessible by the public
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.
I have found solution in git and applied to my project. its working as expected.
Below code copied from there.
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();