Search code examples
asp.net-coreswaggerswashbuckle.aspnetcore

ASP.NET Core API: Authentication popup is not showing up in Swagger UI


I have an ASP.NET Core Web API with Swagger integrated using Swashbuckle. I have successfully integrated authorization on Swagger UI using an operation filter, because I do not want to show padlock for anonymous APIs.

.OperationFilter<AuthorizeFilter>()

Inside the filter, I have registered basic auth security requirement for Swagger UI.

My problem is, even though authentication is happening in APIs on Swagger UI, I no longer see that nice authentication popup which is giving when click on the padlock icon.

Could someone answer, why I am not seeing the auth popup now?


Solution

  • Update 2023-04-17: Updated security definition type to Http as per @mimmoz81's suggestion


    Assuming you have some endpoints that protected with [Authorize] attribute (can also be put on the controller).

    [Route("")]
    public class HelloController : ControllerBase
    {
        [Authorize]
        [HttpGet("secure")]
        public IActionResult GetSomethingPrivate()
        {
            return Ok("secret");
        }
    
        [HttpGet("public")]
        public IActionResult GetSomethingPublic()
        {
            return Ok("hey");
        }
    }
    

    You need to define a security scheme suitable for your needs. But do not require it globally, instead add it inside an operation filter. Here I've added a simple token auth:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
            c.AddSecurityDefinition("token", new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                In = ParameterLocation.Header,
                Name = HeaderNames.Authorization,
                Scheme = "Bearer"
            });
            // dont add global security requirement
            // c.AddSecurityRequirement(/*...*/);
            c.OperationFilter<SecureEndpointAuthRequirementFilter>();
        });
    }
    

    And here's the operation filter which references the token auth scheme we've just created. It checks if the endpoint needs authentication, then adds the requirement.

    internal class SecureEndpointAuthRequirementFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (!context.ApiDescription
                .ActionDescriptor
                .EndpointMetadata
                .OfType<AuthorizeAttribute>()
                .Any())
            {
                return;
            }
    
            operation.Security = new List<OpenApiSecurityRequirement>
            {
                new OpenApiSecurityRequirement
                {
                    [new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "token" }
                    }] = new List<string>()
                }
            };
        }
    }
    

    When you run the app, it works as you expect:

    swagger ui

    So does the auth popup:

    auth popup

    Bonus: using basic auth

    Define a new security scheme with following values:

    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        services.AddSwaggerGen(c =>
        {
            // ...
            // basic auth scheme (username + password)
            c.AddSecurityDefinition("basic", new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                Scheme = "basic"
            });
            // dont add global security requirement
            // c.AddSecurityRequirement(/*...*/);
            c.OperationFilter<SecureEndpointAuthRequirementFilter>();
        });
    }
    

    Then update the operation filter to reference basic auth scheme:

    internal class SecureEndpointAuthRequirementFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (!context.ApiDescription
                .ActionDescriptor
                .EndpointMetadata
                .OfType<AuthorizeAttribute>()
                .Any())
            {
                return;
            }
    
            operation.Security = new List<OpenApiSecurityRequirement>
            {
                new OpenApiSecurityRequirement
                {
                    [new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme, 
                            Id = "basic" // <-- changed "token" -> "basic"
                        }
                    }] = new List<string>()
                }
            };
        }
    }
    

    here's how the auth popup looks:

    basic auth popup

    After logging in, requests include the correct Authorization header.

    header