Use JWT Bearer Authorization in Swagger OpenAPI

In this tutorial, we will implement a CRUD functionality with JWT Authentication in ASP NET Core WEB API and use JWT Bearer Authorization in Swagger for testing. In the past, we’ve created an article that uses JWT Authentication in ASP.NET Core, which can be found here. This tutorial just extends that topic and adds swagger to test our Web API project. We will also include how to configure Swagger to accept a Header Authorization to test the method within our API with Authorize attributes.

What is Swagger?

Swagger is an open-source set of rules, specifications, and tools for developing and describing RESTful APIs. The Swagger framework allows developers to create interactive, machine, and human-readable API documentation. Below is the swagger UI with our default methods and properties or this tutorial.

Swagger Dashboard - Use JWT Bearer Authorization in Swagger OpenAPI
Swagger

Before we start please make sure you have the following installed on your machine.

  • The latest version of Visual Studio
  • Alternatively, you can also use the Visual Studio Code.
  • SQL Server

In this tutorial, I am going to use Visual Studio 2019. Below is the video from my previous article on how to implement JWT Auth in ASP.NET Core.

I. Create and Setup a new ASP.NET Core Web API

  1. First, create your ASP.NET Core Web API. To do that just follow the steps below.
  • Select File > New > Project.
  • Select ASP.NET Core Web Application. Name the project JWTAuthDemo to have the same namespace as my project. Click OK.
  • Select API and then uncheck Configure for HTTPS.
  • Lastly, Click on Create.
Create new ASP.NET Core WEB API

2. Now we need to install required packages for this project. I have listed it all below.

  • Dapper
  • Microsoft.EntityFrameworkCore.SqlServer.Design
  • Microsoft.AspNetCore.Authentication.JwtBearer
  • Swashbuckle.AspNetCore
  • Swashbuckle.AspNetCore.Swagger
  • Swashbuckle.Core
  • System.IdentityModel.Tokens.Jwt

Use NuGet Package Manager to install this package. If this is your first time using this, you can visit this article.

NuGet Packages

II. Setup Appsettings.Json

After installing all the necessary packages that we need, add your SQL connection string and JWT Auth key inside appsettings.json.

"JwtAuth": {
     "Key": "freecodeSecretKey",
     "Issuer": "freecodespot.com"
   },
   "ConnectionStrings": {
     "default": "Server=CODERSIGN\SQLEXPRESS01;Database=JWTDemoDB;User Id=freecode;Password=freecodespot;"
   }

We will use this later on the code part of this tutorial. See the screenshot below.

appsettings.json
appsettings.json

III. Register JWT base authentication

First, we need to configure JWT based authentication in our project. To do that, register JWT authentication on your startup.cs. Put the code snippet below under ConfigureServices.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)    
            .AddJwtBearer(options =>    
            {    
                options.TokenValidationParameters = new TokenValidationParameters    
                {    
                    ValidateIssuer = true,    
                    ValidateAudience = true,    
                    ValidateLifetime = true,    
                    ValidateIssuerSigningKey = true,    
                    ValidIssuer = Configuration["JwtAuth:Issuer"],    
                    ValidAudience = Configuration["JwtAuth:Issuer"],    
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtAuth:Key"]))    
                };    
            });
  • ValidateIssuer = true » Validate the server that generates the token.
  • ValidateAudience = true » Validate the recipient of the token is authorized to receive.
  • ValidateLifetime = true » Check if the token is not expired and the signing key of the issuer is valid
  • ValidateIssuerSigningKey = true » Validate signature of the token
  • ValidIssuer » stored in appsettings.json
  • ValidAudience » stored in appsettings.json
  • IssuerSigningKey » stored in appsettings.json

IV. Create Model properties

Let’s create model properties for this project. We will use this model later to handle request parameters and response in our web API. See the code snippet below.

Model Property

LoginModel

This model will handle the parameter request for our API login method.
using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Threading.Tasks;
 namespace JWTAuthDemo.Models
 {
     public class LoginModel
     {
         [Required]
         public string email { get; set; }
         [Required]
         public string password { get; set; }
     }
 }

User

This Model will handle the response from our tbl_users table
using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Threading.Tasks;
 namespace JWTAuthDemo.Models
 {
     public class User
     {
         [Required]
         public string Username { get; set; }
         [Required]
         public string Password { get; set; }
         [Required]
         public string Email { get; set; }
         [Required]
         public string Role { get; set; }
         public DateTime Date { get; set; } = DateTime.Now;
     }
 }

Response

This will act as the property container for our Repository class response
using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 namespace JWTAuthDemo.Models
 {
     public class Response<T>
     {
         public T Data { get; set; }
         public string message { get; set; }
         public int  code { get; set; }
     }
 }

What is <T>?

T is called a type parameter, which can be used as a type of fields, properties, method parameters, return types, and delegates in the DataStore class. For example, Data is generic property because we have used a type parameter T as its type instead of the specific data type.

V. Create Table and Stored Procedures

Let’s create a database, table, and stored procedure that we need for this tutorial. Below are the table and procedures that we need.

  1. Create a database using the query below.
CREATE DATABASE JWTDemoDB;

2. Create tbl_users table inside JWTDemoDB Database.

USE [JWTDemoDB]
 GO
 SET ANSI_NULLS ON
 GO
 SET QUOTED_IDENTIFIER ON
 GO
 CREATE TABLE [dbo].[tbl_users](
     [userid] [int] IDENTITY(1,1) NOT NULL,
     [username] nvarchar NULL,
     [password] nvarchar NULL,
     [email] nvarchar NULL,
     [role] nvarchar NULL,
     [reg_date] [datetime] NULL
 ) ON [PRIMARY]
 GO

3. Create a stored procedure for login action. Name it sp_loginUser.

USE [JWTDemoDB]
 GO
 SET ANSI_NULLS ON
 GO
 SET QUOTED_IDENTIFIER ON
 GO
  -- =============================================
  -- Author:    FreeCode Spot
  -- Create date: 
  -- Description:    
  -- =============================================
  CREATE PROCEDURE [dbo].[sp_loginUser]
      @email Nvarchar(50),
      @password nvarchar(200),
      @retVal int OUTPUT
      AS
  BEGIN
      SET NOCOUNT ON;
         Select 
                userid,
                username,
                email,
                [role],
                reg_date 
                FROM tbl_users 
                where Email = @email and 
               [Password] = CONVERT(VARCHAR(32), HashBytes('MD5', @password), 2)
         IF(@@ROWCOUNT > 0)
         BEGIN
          SET @retVal = 200
         END
         ELSE
         BEGIN
           SET @retVal = 500
         END
  END

4. Create a stored procedure for the registration of the user. Name it sp_registerUser. See the code snippet below.

USE [JWTDemoDB]
 GO
 SET ANSI_NULLS ON
 GO
 SET QUOTED_IDENTIFIER ON
 GO
  -- =============================================
  -- Author:        FeeCode Spot
  -- Create date: 
  -- Description:    
  -- =============================================
  CREATE PROCEDURE [dbo].[sp_registerUser]
      @username Nvarchar(50),
      @email Nvarchar(50),
      @password nvarchar(200),
      @role nvarchar(50),
      @retval int OUTPUT
  AS
  BEGIN
      SET NOCOUNT ON;
        INSERT INTO tbl_users(
                              username,
                              Email,
                              [Password],
                              [role],
                              reg_date) 
                              VALUES(
                              @username,
                              @email,
                              CONVERT(VARCHAR(32), HashBytes('MD5', @password), 2),
                              @role,
                              GETDATE())
        if(@@ROWCOUNT > 0)
        BEGIN
          SET @retval = 200 --return value when changes is detected on table
        END
        ELSE
        BEGIN
        SET @retval = 500  --return value if something went wronng
        END
  END

5. Create a stored procedure for deletion of user. Name it sp_deleteUser. See the code snippet below.

 USE [JWTDemoDB]
 GO
 SET ANSI_NULLS ON
 GO
 SET QUOTED_IDENTIFIER ON
 GO
  -- =============================================
  -- Author:        FeeCode Spot
  -- Create date: 
  -- Description:    
  -- =============================================
  ALTER PROCEDURE [dbo].[sp_deleteUser]
      @userid int,
      @retval int OUTPUT
  AS
  BEGIN
      SET NOCOUNT ON;
           Delete tbl_users where userid = @userid
        
           if(@@ROWCOUNT > 0)
        BEGIN
          SET @retval = 200 --return value when changes is detected on table
        END
        ELSE
        BEGIN
        SET @retval = 500  --return value if something went wronng
        END
  END 

VI. Create Repository Class

The repository class will handle the authentication and CRUD functionality of our WEB API. This repository class will inherit from an interface class. To create this class, follow the steps below.

Repository Class - Use JWT Bearer Authorization in Swagger OpenAPI
  1. Create a Repository Folder from your project’s root directory.
  2. Create New Interface and name it IJWTAuthManager. See the code snippet below.
 using Dapper;
 using JWTAuthDemo.Models;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
  
 namespace JWTAuthDemo.Repository
 {
     public interface IJWTAuthManager
     {
        Response<string> GenerateJWT(User user);
        Response<T> Execute_Command<T>(string query, DynamicParameters sp_params);
        Response<List<T>> getUserList<T>();
     }
 } 

3. After that, create another file name JWTAuthManager. This class will be the implementation of the IJWTAuthManager interface. Below is the method we added inside JWTAuthManager class.

MethodFunctions
GenerateJWT(User user)Generate JSON Web Token for the authenticated user
Execute_Command(string query, DynamicParameters sp_params)Execute stored procedures using dapper
getUserList()Retrieve a list of the user using dapper
Methods inside JWTAuthManager
using Dapper;
using JWTAuthDemo.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
  
 namespace JWTAuthDemo.Repository
 {
     public class JWTAuthManager : IJWTAuthManager
     {
         private readonly IConfiguration _configuration;
  
         public JWTAuthManager(IConfiguration configuration)
         {
             _configuration = configuration;
         }
         public Response<string> GenerateJWT(User user)
         { 
             Response<string> response = new Response<string>();
  
             var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtAuth:Key"]));
             var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
  
             //claim is used to add identity to JWT token
             var claims = new[] {
                 new Claim(JwtRegisteredClaimNames.Sub, user.Username),
                 new Claim(JwtRegisteredClaimNames.Email, user.Email),
                 new Claim("roles", user.Role),
                 new Claim("Date", DateTime.Now.ToString()),
                 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
             };
  
             var token = new JwtSecurityToken(_configuration["JwtAuth:Issuer"],
               _configuration["JwtAuth:Issuer"],
               claims,    //null original value
               expires: DateTime.Now.AddMinutes(120),
               signingCredentials: credentials);
  
             response.Data = new JwtSecurityTokenHandler().WriteToken(token); //return access token
             response.code = 200;
             response.message = "Token generated";
             return response;
         }
  
         public Response<T> Execute_Command<T>(string query, DynamicParameters sp_params)
         {
             Response<T> response = new Response<T>();
             using (IDbConnection dbConnection = new SqlConnection(_configuration.GetConnectionString("default")))
             {
                 if (dbConnection.State == ConnectionState.Closed)
                     dbConnection.Open();
                using var transaction = dbConnection.BeginTransaction();
                 try
                 {
                     response.Data = dbConnection.Query<T>(query, sp_params, commandType: CommandType.StoredProcedure, transaction: transaction).FirstOrDefault();
                     response.code = sp_params.Get<int>("retVal"); //get output parameter value
                     response.message = "Success";
                     transaction.Commit();
                 }
                 catch (Exception ex)
                 {
                     transaction.Rollback();
                     response.code = 500;
                     response.message = ex.Message;
                 }
             }
             return response;
         }
  
         public Response<List<T>> getUserList<T>()
         {
             Response<List<T>> response = new Response<List<T>>();
             using IDbConnection db = new SqlConnection(_configuration.GetConnectionString("default"));
             string query = "Select userid,username,email,[role],reg_date FROM tbl_users";
             response.Data = db.Query<T>(query, null, commandType: CommandType.Text).ToList();
             return response;
         }
     }
 }

VII. Create AccountController

AccountController will handle incoming HTTP requests and send responses back to the caller. Create a new API controller inside the controller’s folder. Below is the CRUD action result method that I have inside my AccountController.

Note that we also apply role-based authorization in this tutorial. The user can only access the method that is specified in Authorize Roles per method.

login

Login method will handle the authentication and the generation the JWT.
 [HttpPost("Login")]
 [AllowAnonymous]
 public IActionResult login([FromBody]LoginModel user)
    {
     if (!ModelState.IsValid)
        {
           return BadRequest("Parameter is missing");
        }
  
    DynamicParameters dp_param = new DynamicParameters();
    dp_param.Add("email",user.email,DbType.String);
    dp_param.Add("password",user.password,DbType.String);
    dp_param.Add("retVal", DbType.String, direction: ParameterDirection.Output);
    var result = _authentication.Execute_Command<User>("sp_loginUser",dp_param);
    if (result.code == 200)
    {
        var token = _authentication.GenerateJWT(result.Data);
        return Ok(token);
    }
      return NotFound(result.Data);
  } 

UserList – Role » Only User is allowed to access this method

Retrieve User list from the database.
 [HttpGet("UserList")]
 [Authorize(Roles = "User")]
 public IActionResult getAllUsers()
 {
     var result = _authentication.getUserList<User>();
     return Ok(result);
 } 

Register – Role » Only Admin can access this method

This will register user to the database
 [HttpPost("Register")]
 [Authorize(Roles = "Admin")]
 public IActionResult Register([FromBody]User user)
 {
     if (!ModelState.IsValid)
     {
         return BadRequest("Parameter is missing");
     }
     DynamicParameters dp_param = new DynamicParameters();
     dp_param.Add("email", user.Email, DbType.String);
     dp_param.Add("username", user.Username, DbType.String);
     dp_param.Add("password", user.Password, DbType.String);
     dp_param.Add("role", user.Role, DbType.String);
     dp_param.Add("retVal", DbType.String, direction: ParameterDirection.Output);
     var result = _authentication.Execute_Command<User>("sp_registerUser", dp_param);
     if (result.code == 200)
     {
         return Ok(result);
     }
     return BadRequest(result);
 } 

Delete – Role » Only Admin can access this method

This will remove user from the database
 [HttpDelete("Delete")]
 [Authorize(Roles = "Admin")]
 public IActionResult Delete(string id)
 {
     if (id == string.Empty)
     {
         return BadRequest("Parameter is missing");
     }
  
     DynamicParameters dp_param = new DynamicParameters();
     dp_param.Add("userid", id, DbType.String);
   
     dp_param.Add("retVal", DbType.String, direction: ParameterDirection.Output);
     var result = _authentication.Execute_Command<User>("sp_deleteUser", dp_param);
     if (result.code == 200)
     {
         return Ok(result);
     }
     return NotFound(result);
 } 

Below are the full source code from my AccountController.

  using Dapper;
 using JWTAuthDemo.Models;
 using JWTAuthDemo.Repository;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Linq;
 using System.Threading.Tasks;
  
 namespace JWTAuthDemo.Controllers
 {
     [Route("api/[controller]")]
     [ApiController]
     
     public class AccountController : ControllerBase
     {
         private readonly IJWTAuthManager _authentication;
  
         public AccountController(IJWTAuthManager authentication)
         {
             _authentication = authentication;
         }
  
         [HttpPost("Login")]
         [AllowAnonymous]
         public IActionResult login([FromBody]LoginModel user)
         {
             if (!ModelState.IsValid)
             {
                 return BadRequest("Parameter is missing");
             }
  
             DynamicParameters dp_param = new DynamicParameters();
             dp_param.Add("email",user.email,DbType.String);
             dp_param.Add("password",user.password,DbType.String);
             dp_param.Add("retVal", DbType.String, direction: ParameterDirection.Output);
             var result = _authentication.Execute_Command<User>("sp_loginUser",dp_param);
             if (result.code == 200)
             {
                 var token = _authentication.GenerateJWT(result.Data);
                 return Ok(token);
             }
             return NotFound(result.Data);
         }
  
         [HttpGet("UserList")]
         [Authorize(Roles = "User")]
         public IActionResult getAllUsers()
         {
             var result = _authentication.getUserList<User>();
             return Ok(result);
         }
  
         [HttpPost("Register")]
         [Authorize(Roles = "Admin")]
         public IActionResult Register([FromBody]User user)
         {
             if (!ModelState.IsValid)
             {
                 return BadRequest("Parameter is missing");
             }
  
             DynamicParameters dp_param = new DynamicParameters();
             dp_param.Add("email", user.Email, DbType.String);
             dp_param.Add("username", user.Username, DbType.String);
             dp_param.Add("password", user.Password, DbType.String);
             dp_param.Add("role", user.Role, DbType.String);
             dp_param.Add("retVal", DbType.String, direction: ParameterDirection.Output);
             var result = _authentication.Execute_Command<User>("sp_registerUser", dp_param);
             if (result.code == 200)
             {
                 return Ok(result);
             }
             return BadRequest(result);
         }
  
         [HttpDelete("Delete")]
         [Authorize(Roles = "Admin")]
         public IActionResult Delete(string id)
         {
             if (id == string.Empty)
             {
                 return BadRequest("Parameter is missing");
             }
  
             DynamicParameters dp_param = new DynamicParameters();
             dp_param.Add("userid", id, DbType.String);
           
             dp_param.Add("retVal", DbType.String, direction: ParameterDirection.Output);
             var result = _authentication.Execute_Command<User>("sp_deleteUser", dp_param);
             if (result.code == 200)
             {
                 return Ok(result);
             }
             return NotFound(result);
         }
     }
 }

VIII. Configure Swagger to accept Header Authorization

If you created your project using ASP.NET Core 3.1 or 5.0, swagger is already included during creation, so when you run your project, you will see that swagger is the default homepage of your ASP.NET Core Web API. If not, you may also install swagger using NuGet Package Manager in Visual Studio.

Use JWT Bearer Authorization in Swagger OpenAPI

Since we are using JWT Authentication, All the authorize request should contain an authorization header. Which in this case, it is the token that is generated from the login method. Let’s add some minor configuration in our startup.cs. Just follow the steps below. This step will add the Authorize function to the swagger framework.

  1. Open startup.cs and under ConfigureServices method find service.AddSwaggerGen.
  2. Once you locate the swagger configuration, add the following code inside the service.AddSwaggerGen.
 option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
 {
     Name = "Authorization",
     Type = SecuritySchemeType.ApiKey,
     Scheme = "Bearer",
     BearerFormat = "JWT",
     In = ParameterLocation.Header,
     Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 1safsfsdfdfd\"",
 });
 option.AddSecurityRequirement(new OpenApiSecurityRequirement
 {
     {
           new OpenApiSecurityScheme
             {
                 Reference = new OpenApiReference
                 {
                     Type = ReferenceType.SecurityScheme,
                     Id = "Bearer"
                 }
             },
             new string[] {}
     }
 }); 

3. Now, make sure that inside Configure method you will have this code declaration for swagger.

app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWTAuthDemo v1"));

4. Try running your Web API Application. You should now see an Authorize button. See the code snippet below.

JWT Authentication in ASP NET Core WEB API and Swagger Header Authorization

5. Now, let’s Use JWT Bearer Authorization in Swagger. To Authorize your request, run the Login method. Use the generated token from the response. The format should be “Bearer 123xyzx2sff”. Press the Authorize button to set your Authorization header on all the requests from methods displayed in a swagger dashboard. See the screenshot below.

JWT Authentication in ASP NET Core WEB API and Swagger Header Authorization

That’s how you can configure JWT Authentication in ASP.NET Core WEB API and use JWT Bearer Authorization in Swagger. If somehow it doesn’t work on your end. You can download the source code from my GitHub Repository. @coderbugzz.

Summary

We have learned from this tutorial how to use JWT Authentication in ASP NET Core WEB API and use JWT Bearer Authorization in Swagger. We also use role-based authorization in this project tutorial. You can use this to restrict API users to limit them on what method is accessible for them. We also see how Swagger can help us automatically document our API configuration, like the list of methods available and schema property that is required and not. Hopefully, you have learned something from this article.

KEEP CODING!