Empty Available Authorization when using Swashbuckle Swagger .NET 6

When I learned to implement authorization in my WebAPI and tested it in Swagger, I noticed the “Available authorization” page was empty (after clicking the lock icon):

Empty Authorization dialog

For those who don’t know Swagger, you can think of this as a framework that offers a systematic way of notating the interface of any RESTful service under the OpenAPI specification. Today I will not talk in-depth (but you can follow this link if you want to) about how to build API and use Swagger. But more into the problem that I got earlier.

The root cause of the problem (Available authorization empty) is that the authorization response can’t identify your setup (based on this thread in StackOverflow and a reply by ahmeticat). 

You need to make sure you install the below NuGet packages:

<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.2" />

For Authorization Logic, I create the below class (I’m using MediatR):

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using LearningCqrs.Core;
using LearningCqrs.Data;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;

namespace LearningCqrs.Features.Users;

public class Authorize
{
    public record AuthorizeCommand(string Username, string Password) : IRequest<string>;

    public class AuthorizeHandler : IRequestHandler<AuthorizeCommand, string>
    {
        private readonly BlogContext _dbContext;

        public AuthorizeHandler(BlogContext dbContext)
        {
            _dbContext = dbContext;
        }
        
        public async Task<string> Handle(AuthorizeCommand request, CancellationToken cancellationToken)
        {
            var user = await _dbContext.Users.SingleAsync(e => e.Username == request.Username, cancellationToken);
            var passwordHasher = new PasswordHasher<User>();
            var result = passwordHasher.VerifyHashedPassword(user, user.Password, request.Password);
            
            if(result == PasswordVerificationResult.Failed)  throw new InvalidOperationException("Username or Password is incorrect");

            var claims = new[]
            {
                new Claim(ClaimTypes.Name, request.Username),
                new Claim(ClaimTypes.Role, Settings.Role)
            };

            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration.GetConnectionString("AppId")));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
            var tokenDescriptor = new JwtSecurityToken(issuer: Configuration.GetConnectionString("ValidIssuer"), 
                audience: Configuration.GetConnectionString("ValidAudience"), claims, expires: DateTime.Now.AddHours(2), 
                signingCredentials: credentials);
            return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
        }
    }
}

Then you need to create AuthResponsesOperationFilter to Filter the correct Auth result in the UI:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Net.Http.Headers;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace LearningCqrs.Core;

// https://stackoverflow.com/questions/49908577/empty-authorization-header-on-requests-for-swashbuckle-aspnetcore
public class AuthResponsesOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var authAttributes = context.MethodInfo.DeclaringType?.GetCustomAttributes(true)
            .Union(context.MethodInfo.GetCustomAttributes(true))
            .OfType<AuthorizeAttribute>();

        if (authAttributes == null || !authAttributes.Any()) return;
        
        var securityRequirement = new OpenApiSecurityRequirement()
        {
            {
                // Put here you own security scheme, this one is an example
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = JwtBearerDefaults.AuthenticationScheme
                    },
                    Scheme =JwtBearerDefaults.AuthenticationScheme,
                    Name = HeaderNames.Authorization,
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.Http,
                    BearerFormat = "JWT"
                },
                new List<string>()
            }
        };
            
        operation.Security = new List<OpenApiSecurityRequirement> { securityRequirement };
        operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
    }
}

Here is my AddSwaggerGen code:

serviceCollection.AddSwaggerGen(opt =>
{
	var securityDefinition = new OpenApiSecurityScheme
	{
		Type = SecuritySchemeType.Http,
		In = ParameterLocation.Header,
		Name = HeaderNames.Authorization,
		Scheme = JwtBearerDefaults.AuthenticationScheme
	};
	opt.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, securityDefinition);
	opt.AddSecurityRequirement(new OpenApiSecurityRequirement
	{
		{securityDefinition, Array.Empty<string>()}
	});

	opt.OperationFilter<AuthResponsesOperationFilter>();
});

Because you want to implement Authorization, you need to add AuthorizeAttribute in your Controller. Below is the sample of my UsersController:

using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace LearningCqrs.Controllers;

[Authorize]
[Route("api/users")]
[ApiController]
public class UsersController : ControllerBase
{
    private readonly IMediator _mediator;

    public UsersController(IMediator mediator)
    {
        _mediator = mediator;
    }
    
    [AllowAnonymous]
    [HttpPost("authenticate")]
    public async Task<ActionResult> Authenticate([FromBody]Features.Users.Authorize.AuthorizeCommand authorizeCommand, 
        CancellationToken cancellationToken)
    {
        try
        {
            var result = await _mediator.Send(authorizeCommand, cancellationToken);
            return Ok(result);
        }
        catch (Exception e)
        {
            return BadRequest(e.Message);
        }
    }
    
    [HttpPost]
    public async Task<ActionResult> AddProduct([FromBody] Features.Users.Create.CreateUserCommand createUserCommand, 
        CancellationToken cancellationToken)
    {
       var result = await _mediator.Send(createUserCommand, cancellationToken);
       return Ok(result);
    }
    
    [HttpGet("getbyusername")]
    public async Task<ActionResult> GetUserByUsername([FromQuery] Features.Users.GetByUsername.GetByUsernameQuery getByUsernameQuery, 
        CancellationToken cancellationToken)
    {
        var result = await _mediator.Send(getByUsernameQuery, cancellationToken);
        return Ok(result);
    }
}

And here is the result:

The result

Happy coding!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.