Introduction
In the previous part of the tutorial we learned about how to implement JWT access tokens; In this step-by-step tutorial, I will explain how to use the identity framework with refresh token validations. In this tutorial, we cover the following points.
- Create a user with an identity framework with custom fields and their use.
- Create a refresh token with validations of user credentials
- How to authorize any web API endpoint
- Validate refresh token and generate a new access token.
Before we start, let’s understand what is refresh token how it works. Let’s look at the following flow diagram
![Flow Diagram]()
As shown in the above flow diagram there are two flows users with an access token and without an access token.
- User without access token:
- User can send a request to generate a token with its credentials
- The server will check credentials and if the credential is validated then generate token
- With validation, token users can access resources
- With access token:
- Validate access token if the token is valid then user access resources
- If the token is not valid show an error to the user.
- in case of an expired token user can send a request to regenerate the token with a refresh token.
- Server will check refresh token validity and if valid generate a new access token and refresh token
- So with the new token users can easily get resources.
Step 1 - Install NuGet Packages
Microsoft.AspNetCore.Identity
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.Extensions.Identity.Core
Npgsql.EntityFrameworkCore.PostgreSQL
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Tools
Npgsql.EntityFrameworkCore.PostgreSQL.Design
Step 2 - Create Class for users, DBContext, and generate Database
Create a new class named Application user
![Add Application User class]()
And Following code to applications class
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations.Schema;
namespace WebApplication.DataLayer
{
[Table("ApplicationUser")]
public class ApplicationUser:IdentityUser
{
[Column("FirstName")]
public string FirstName { get; set; }
[Column("LastName")]
public string LastName { get; set; }
[Column("RefreshToken")]
public string? RefreshToken { get; set; }
[Column("RefreshTokenValidity")]
public DateTime? RefreshTokenValidity { get; set; }
}
}
In the above code, we are using Guide as the Primary key and adding new two fields for first name and last name.
The next step is to add a new class for DB Context with the following code.
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace WebApplication.DataLayer
{
public class RefreshTokenDemoContext : IdentityDbContext<ApplicationUser>
{
public RefreshTokenDemoContext()
{
}
public RefreshTokenDemoContext(DbContextOptions<RefreshTokenDemoContext> options) : base(options)
{
}
public DbSet<ApplicationUser> applicationUsers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUserLogin<Guid>>().HasNoKey();
modelBuilder.Entity<IdentityUserClaim<Guid>>().HasNoKey();
modelBuilder.Entity<IdentityUserToken<Guid>>().HasNoKey();
modelBuilder.Entity<IdentityRoleClaim<Guid>>().HasNoKey();
base.OnModelCreating(modelBuilder);
}
}
}
Add a Connection string to Appsettings.Json Files
"ConnectionStrings": {
"RefreshTokenDB": "Host=localhost;Port=5432;Database=RefreshTokenDB;Username=postgres;Password=admin123"
},
After adding the connection string app settings file look alike following
![AppSettings]()
Create an Extension class and method to add Settings in Dependency injections
Right-click solution Add Class- > Named IdentityServicesExtensions with following code
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using WebApplication.DataLayer;
using Microsoft.AspNetCore.Identity;
namespace WebApplication.Extensions
{
public static class IdentityServicesExtensions
{
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<RefreshTokenDemoContext>(options =>
options.UseNpgsql(configuration.GetConnectionString("RefreshTokenDB")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<RefreshTokenDemoContext>();
return services;
}
}
}
The next step is to call this extension method in Program.cs file. I m using the extension method because it will not make you program.cs file messy. add the following line.
builder.Services.AddIdentityServices(builder.Configuration);
Once you have added these to your program. cs will be.
![Preview Program.cs]()
Run the Migration to create a database here we are using database code first migration to generate the database.
First Run the add-migration command to then update the database command.
To Run Migration open the Package manager console from Tool> Package manager > Package Manager Console.
![]()
Then Run Commands these will output like below.
![Run migration]()
And after successfully adding the migration run command to update the database it will create a database
![]()
Step 3 - Register User and Generate Refresh Token
In the Accounts, the controller adds a variable for DbContext class and UserManger like below.
private readonly JwtSettings jwtSettings;
private readonly UserManager<ApplicationUser> userManager;
private readonly RefreshTokenDemoContext refreshTokenDemoContext;
public AccountController(JwtSettings jwtSettings
, UserManager<ApplicationUser> userManager
, RefreshTokenDemoContext refreshTokenDemoContext)
{
this.jwtSettings = jwtSettings;
this.userManager = userManager;
this.refreshTokenDemoContext = refreshTokenDemoContext;
}
Add a method to register a user with the following code.
[HttpPost]
public async Task<IActionResult> RegisterUser(Users users)
{
try
{
var applicationUser = new ApplicationUser()
{
UserName = users.UserName,
Email = users.EmailId,
FirstName = users.FirstName,
LastName = users.LastName,
EmailConfirmed = true,
TwoFactorEnabled = false,
LockoutEnabled = false,
};
var result = await userManager.CreateAsync(applicationUser, users.Password);
if (result.Succeeded)
{
return Ok("User created");
}
else
{
return BadRequest(result.Errors)
}
}
catch (Exception)
{
throw;
}
}
For generating token replace get token method with following code.
[HttpPost]
public async Task<IActionResult> GetToken(UserLogins userLogins)
{
try
{
var Token = new UserTokens();
var user = await userManager.FindByNameAsync(userLogins.UserName);
if (user == null)
{
return BadRequest("User name is not valid");
}
var Valid = await userManager.CheckPasswordAsync(user, userLogins.Password);
if (Valid)
{
var strToken = Guid.NewGuid().ToString();
var validity = DateTime.UtcNow.AddDays(15);
Token = JwtHelpers.JwtHelpers.GenTokenkey(new UserTokens()
{
EmailId = user.Email,
GuidId = Guid.NewGuid(),
UserName = user.UserName,
Id = Guid.Parse(user.Id),
RefreshToken = strToken,
}, jwtSettings);
var tokenupdate = refreshTokenDemoContext.Users.Where(x => x.Id == user.Id).FirstOrDefault();
tokenupdate.RefreshToken = strToken;
tokenupdate.RefreshTokenValidity = validity;
refreshTokenDemoContext.Update(tokenupdate);
refreshTokenDemoContext.SaveChanges();
Token.RefreshToken = strToken;
}
else
{
return BadRequest($"wrong password");
}
return Ok(Token);
}
catch (Exception)
{
throw;
}
}
For generating access token from refresh token add following method.
/// <summary>
/// Generate an Access token
/// </summary>
/// <param name="userLogins"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> RefreshToken(RefreshTokenModel userLogins)
{
try
{
var Token = new UserTokens();
var user = await userManager.FindByNameAsync(userLogins.UserName);
if (user == null)
{
return BadRequest("User name is not valid");
}
var Valid = refreshTokenDemoContext.Users.Where(x=>x.UserName==userLogins.UserName
&& x.RefreshToken==userLogins.RefreshToken
&& x.RefreshTokenValidity>DateTime.UtcNow).Count()>0;
if (Valid)
{
var strToken = Guid.NewGuid().ToString();
var validity = DateTime.UtcNow.AddDays(15);
Token = JwtHelpers.JwtHelpers.GenTokenkey(new UserTokens()
{
EmailId = user.Email,
GuidId = Guid.NewGuid(),
UserName = user.UserName,
Id = Guid.Parse(user.Id),
RefreshToken = strToken,
}, jwtSettings);
var tokenupdate = refreshTokenDemoContext.Users.Where(x => x.Id == user.Id).FirstOrDefault();
tokenupdate.RefreshToken = strToken;
tokenupdate.RefreshTokenValidity = validity;
refreshTokenDemoContext.Update(tokenupdate);
refreshTokenDemoContext.SaveChanges();
}
else
{
return BadRequest($"wrong password");
}
return Ok(Token);
}
catch (Exception)
{
throw;
}
}
Step 4 - Validate Refresh Token
The last step is checking your flow.
First, create a new User.
![]()
![]()
Get tokens by using a newly created user credential.
![]()
Now without validating the token string try to get the user list, and it will show an error.
![]()
Validate the token string
![]()
Now try to get the user list it will display the user list after the access token expired it will show an error like the below.
![]()
Generate new access token from refresh token bypassing access token and refresh token,
![]()
After regenerating the token validates the token so you get a list of users
Thank You. Happy Coding.
Summary
In this article, I discussed how we can create a JWT access token. We also saw how we can authorize the WEB API endpoint.