Darshani Jayasekara
Darshani Jayasekara

Reputation: 741

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?

Upvotes: 10

Views: 17188

Answers (4)

abdusco
abdusco

Reputation: 11061

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

Upvotes: 16

mimmoz81
mimmoz81

Reputation: 51

@abdusco answer was really great, but I had to slightly modify to SecuritySchemeType.Http, instead of SecuritySchemeType.ApiKey

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, // ----> HAD TO CHANGE THIS TO Http instead of ApiKey
            In = ParameterLocation.Header,
            Name = HeaderNames.Authorization,
            Scheme = "Bearer"
        });
        // dont add global security requirement
        // c.AddSecurityRequirement(/*...*/);
        c.OperationFilter<SecureEndpointAuthRequirementFilter>();
    });
}

Doing this, I actually see the Bearer token auth popup style.

enter image description here

As of swagger docs https://swagger.io/docs/specification/authentication/

enter image description here

Upvotes: 1

Akbar Asghari
Akbar Asghari

Reputation: 681

the Abdusco's answer is true but modify the Apply method like this for AllowAnonymousAttribute Methods in Authorized Controllers

if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any() || context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AllowAnonymousAttribute>()
.Any())
{
    return;
}

Upvotes: 0

Safyan Yaqoob
Safyan Yaqoob

Reputation: 552

In my case, I am using JWT Token Authentication with .NET Core API. I Configure the swagger with the authorization token using the below code. This code will add global security requirements.

In Startup Class ConfigureServices Method.

        //Swagger Configuration

        services.AddSwaggerGen(options =>
        {
            options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
            {
                Title = "API",
                Version = "v2",
                Description = "Your Api Description"
            });
            options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = "JWT Authorization header using the Bearer scheme (Example: 'Bearer 12345abcdef')",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey,
                Scheme = "Bearer"
            });
            options.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        }
                    },
                    Array.Empty<string>()
                }
            });
        });

And In Configure Method

        app.UseSwagger();

        app.UseSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
        });

After running the API project Authorize button will appear on the right side. On Click the authorize button Authorization popup open and then pass the token in the text box with 'Bearer token'.

Authorization working fine for me.

Upvotes: 6

Related Questions