Welcome to today’s post.
I will discuss how to secure Swagger Web APIs using JWT Authorization.
This involves several tasks:
- Implement Web API controllers and methods with authorization attributes used for securing methods.
- Enable security definitions for the Swagger API methods.
- Filter the Swagger API methods to enable authorization for secured operation methods.
- Generate a token from our API.
- Test the secured Swagger API methods using our token.
For the first task, we implement our API controllers to include authorization attributes where required.
We decorate each method with either security authorization or unsecured anonymous access as shown:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.Extensions.Options;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Threading.Tasks;
...
namespace WebApi.Controllers
{
public class UsersController : Controller
{
private IUserService _userService;
private TokenManager _tokenManager;
public UsersController(
IUserService userService,
TokenManager tokenManager)
{
_userService = userService;
_tokenManager = tokenManager;
}
[AllowAnonymous]
[HttpPost("token")]
public IActionResult Token([FromBody]UserViewModel userDto)
{
…
}
…
[HttpGet("api/[controller]/GetAll")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IActionResult GetAll()
{
…
}
…
[HttpGet("api/[controller]/{id}")]
public IActionResult GetById(int id)
{
…
}
…
}
}
For the second task, we implement code to enable Swagger API definitions to be generated for our Web API.
This is done as follows in our Startup.cs:
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.Swagger;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
…
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1",
new Swashbuckle.AspNetCore.Swagger.Info {
Title = "BookLoan Catalog API", Version = "v1"
});
c.AddSecurityDefinition("Bearer",
new ApiKeyScheme() {
In = "header",
Description = "Please enter into field the word 'Bearer' following by space and JWT",
Name = "Authorization",
Type = "apiKey"
});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>> {
{ "Bearer", Enumerable.Empty<string>() },
});
});
…
}
public void Configure(IApplicationBuilder app,
IHostingEnvironment env)
{
…
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json",
"BookLoan Catalog API");
c.RoutePrefix = string.Empty;
});
…
}
When we run our API application the following Swagger operation definitions will show, however all the operation methods will be padlocked which is not what we really want.

To fix the above problem with our third task, we will have to filter out the API methods selectively at runtime to only padlock the API operation methods that have the [Authorize] attribute.
To achieve this we will extend the IOperationFilter interface from the Swashbuckle.AspNetCore.SwaggerGen NuGet library.
The code below will filter each API method checking against the AuthorizeFilter and IAllowAnonymousFilter types (from Microsoft.AspNetCore.Mvc.Authorization):
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
public class AddAuthHeaderOperationFilter : IOperationFilter
{
private readonly IHttpContextAccessor httpContextAccessor;
public AddAuthHeaderOperationFilter(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void Apply(Operation operation,
OperationFilterContext context)
{
var filterDescriptor = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterDescriptor
.Select(filterInfo => filterInfo.Filter)
.Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterDescriptor.Select(
filterInfo => filterInfo.Filter).Any(
filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "Authorization",
In = "header",
Description = "JWT access token",
Required = true,
Type = "string"
});
operation.Responses.Add("401", new Response {
Description = "Unauthorized" });
operation.Responses.Add("403", new Response {
Description = "Forbidden" });
operation.Security = new List<IDictionary<string,
IEnumerable<string>>>();
operation.Security.Add(
new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
}
);
}
}
}
To enable the authorization filter AddAuthHeaderOperationFilter, the following change to the extension method call services.AddSwaggerGen() is required in ConfigureServices():
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info {
Title = "BookLoan Identity API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
{
In = "header",
Description = "Please enter into field the word 'Bearer' following by space and JWT",
Name = "Authorization",
Type = "apiKey"
});
c.OperationFilter<AddAuthHeaderOperationFilter>();
});
When we re-build and run our API application the Swagger methods will show as follows:

The fourth task involves generating the JWT token.
Entering the credentials to your data will give the desired token:

If your current API project is not your identity API then using POSTMAN to access it through an existing deployment (for example on your local IIS) of your identity service is an alternative.

The access token is a lengthy string containing scrambled alphanumeric characters.
The token once generated can be used to test access of our secure methods.
For our final task we will conduct the security test.
Click the padlock on the operation and the following dialog appears:

Enter the bearer authorization token and select Authorize.
Then try the secured API operation:

After clicking on the Try it out link, the authorization token will be prefilled as shown:

Following execution of the API method and provided the token is valid, the operation will succeed.
During debugging, any breakpoints within our protected API method will be accessible:

The result will be a successful response:

We have managed to successfully secure our API using JWT authentication and provide a Swagger UI to allow us to submit API requests with a valid JWT token.
That is all for today’s post.
I hope you found this post useful and informative.

Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.