Authenticate users with JWT (JSON Web Tokens) with .Net and TypeScript
When creating web applications, it is a common thing to provide a user login function.
And when creating user logins it is required to authenticate them probably using a token which contain the user session data and user role data.
When considering the token creation and authenticate it there are several ways and technologies to do and in this blog I would like to explain how to create token and validate it using JWT (JSON Web Tokens). Read about JWT from here
There are several packages in NuGet store that provide JWT features and in this article I'm going to use System.IdentityModel.Tokens.Jwt
Install this in your project using the NuGet package manager.
Ok, now let's get started.
Let's create a class to create token when user log in to the system.
Create a hased key of SHA256 to use when crating the token.
This class will use to validate the created token.
I have created a custom attribute so when the request comes with the token to the controller method, we can check the validation and authenticate user or we can deny the request.
In .Net 4.7 I have found that the overriding method has been changed.
If you are using .Net 4.7 and upper, you have to use this as bellow:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext httpContext)
try
{
var header = httpContext.Request.Headers;
var authValue = httpContext.Request.Headers.Where(a => a.Key == "AuthToken").FirstOrDefault().Value;
if (authValue != null)
{
var token = authValue.First();
var tokenValid = TokenHandler.IsTokenValid(token);
if (!tokenValid)
{
if (httpContext.Response == null)
httpContext.Response = new HttpResponseMessage();
httpContext.Response.Headers.Add("AuthorizeUser", "UnAuthorized");
}
return tokenValid;
}
return true;
}
catch (Exception)
{
throw;
}
}
}
When the token is created we have to store it in the front end because we have to send it with every request to authenticate the user.
So I have saved it in the local storage.
Happy Coding, Peace..
And when creating user logins it is required to authenticate them probably using a token which contain the user session data and user role data.
When considering the token creation and authenticate it there are several ways and technologies to do and in this blog I would like to explain how to create token and validate it using JWT (JSON Web Tokens). Read about JWT from here
There are several packages in NuGet store that provide JWT features and in this article I'm going to use System.IdentityModel.Tokens.Jwt
Install this in your project using the NuGet package manager.
Ok, now let's get started.
1.
Create token
using System;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
private const string Secret = "your secret key"; (24 characters in length)
static JwtSecurityTokenHandler tokenhandler = null;
public static string GenerateToken(string Username, int Id, short TerminalId)
{
try
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var header = new JwtHeader(credentials);
var payload = new JwtPayload
{
{ "Username", Username },
{ "Id", Id.ToString() },
{ "TerminalId", TerminalId.ToString() },
{ "ExpireOn", DateTime.UtcNow.AddYears(1) }
};
var secToken = new JwtSecurityToken(header, payload);
if (tokenhandler == null)
{
tokenhandler = new JwtSecurityTokenHandler();
}
tokenhandler = new JwtSecurityTokenHandler();
var token = tokenhandler.WriteToken(secToken);
return token;
}
catch (Exception ex)
{
Logger.Logger.LogError(ex);
throw;
}
}
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
private const string Secret = "your secret key"; (24 characters in length)
static JwtSecurityTokenHandler tokenhandler = null;
public static string GenerateToken(string Username, int Id, short TerminalId)
{
try
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var header = new JwtHeader(credentials);
var payload = new JwtPayload
{
{ "Username", Username },
{ "Id", Id.ToString() },
{ "TerminalId", TerminalId.ToString() },
{ "ExpireOn", DateTime.UtcNow.AddYears(1) }
};
var secToken = new JwtSecurityToken(header, payload);
if (tokenhandler == null)
{
tokenhandler = new JwtSecurityTokenHandler();
}
tokenhandler = new JwtSecurityTokenHandler();
var token = tokenhandler.WriteToken(secToken);
return token;
}
catch (Exception ex)
{
Logger.Logger.LogError(ex);
throw;
}
}
2. Class to managed logged users
In this scenario I have created a class to save the information about the logged users. So I don't have to retrieve those from the database every time.
public sealed class LoggedUsers
{
private static List<string> loggedUsers = null;
public static void AddLoggedUser(string username)
{
if (loggedUsers == null)
{
loggedUsers = new List<string>();
}
if (!loggedUsers.Contains(username))
{
loggedUsers.Add(username);
}
}
public static bool IsLoggedUser(string username)
{
if (loggedUsers.Contains(username))
{
return true;
}
return false;
}
}
{
private static List<string> loggedUsers = null;
public static void AddLoggedUser(string username)
{
if (loggedUsers == null)
{
loggedUsers = new List<string>();
}
if (!loggedUsers.Contains(username))
{
loggedUsers.Add(username);
}
}
public static bool IsLoggedUser(string username)
{
if (loggedUsers.Contains(username))
{
return true;
}
return false;
}
}
3. Validate Token
public static bool IsTokenValid(string token)
{
if (tokenhandler == null)
{
tokenhandler = new JwtSecurityTokenHandler();
}
var userToken = tokenhandler.ReadJwtToken(token);
if (userToken != null && userToken.Payload != null)
{
if (string.IsNullOrEmpty(userToken.Payload["Username"].ToString())
|| string.IsNullOrEmpty(userToken.Payload["ExpireOn"].ToString()))
{
return false;
}
var isLoggedUser = LoggedUsers.IsLoggedUser(userToken.Payload["Username"].ToString());
var tokenExpireOn = DateTime.Parse(userToken.Payload["ExpireOn"].ToString());
if (isLoggedUser && tokenExpireOn > DateTime.Now)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
{
if (tokenhandler == null)
{
tokenhandler = new JwtSecurityTokenHandler();
}
var userToken = tokenhandler.ReadJwtToken(token);
if (userToken != null && userToken.Payload != null)
{
if (string.IsNullOrEmpty(userToken.Payload["Username"].ToString())
|| string.IsNullOrEmpty(userToken.Payload["ExpireOn"].ToString()))
{
return false;
}
var isLoggedUser = LoggedUsers.IsLoggedUser(userToken.Payload["Username"].ToString());
var tokenExpireOn = DateTime.Parse(userToken.Payload["ExpireOn"].ToString());
if (isLoggedUser && tokenExpireOn > DateTime.Now)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
4. Custom Attribute to Authorize request
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userToken = httpContext.Request.Headers["Authorization"];
var tokenValid = UserTokenManager.IsTokenValid(userToken);
if (!tokenValid)
{
httpContext.Response.AppendHeader("AuthorizeUser", "UnAuthorized");
}
return tokenValid;
}
}
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userToken = httpContext.Request.Headers["Authorization"];
var tokenValid = UserTokenManager.IsTokenValid(userToken);
if (!tokenValid)
{
httpContext.Response.AppendHeader("AuthorizeUser", "UnAuthorized");
}
return tokenValid;
}
}
In .Net 4.7 I have found that the overriding method has been changed.
If you are using .Net 4.7 and upper, you have to use this as bellow:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext httpContext)
try
{
var header = httpContext.Request.Headers;
var authValue = httpContext.Request.Headers.Where(a => a.Key == "AuthToken").FirstOrDefault().Value;
if (authValue != null)
{
var token = authValue.First();
var tokenValid = TokenHandler.IsTokenValid(token);
if (!tokenValid)
{
if (httpContext.Response == null)
httpContext.Response = new HttpResponseMessage();
httpContext.Response.Headers.Add("AuthorizeUser", "UnAuthorized");
}
return tokenValid;
}
return true;
}
catch (Exception)
{
throw;
}
}
}
Read More about creating custom attributes: https://docs.microsoft.com/en-us/dotnet/standard/attributes/writing-custom-attributes
5. Save token in localstorage
private LogIn(){
let login = this.CreateLogInObject();
self.userLogInService.LogIn(login, function (result: ResultObject) {
if(result != null){
if(result.State == 200 && result.ResultData != null){
localStorage.setItem("Token", result.ResultData);
}
}
});
}
let login = this.CreateLogInObject();
self.userLogInService.LogIn(login, function (result: ResultObject) {
if(result != null){
if(result.State == 200 && result.ResultData != null){
localStorage.setItem("Token", result.ResultData);
}
}
});
}
6. Send request to server with the token
We are all set now. From now onwards we have to send the token with every request within the ajax call when calling the server and we are going to send it within the header section off the request.
public TestUserAuthentication = (callback: any) => {
$.ajax({
url: '/Login/TestUserAuthentication',
type: 'GET',
dataType: 'JSON',
headers: {"Authorization": localStorage.getItem('Token')},
success: function (data: ResultObject) {
callback(data);
},
error: function (error) {
let result = new ResultObject();
if(error.getResponseHeader("authorizeuser") != undefined || error.getResponseHeader("authorizeuser") != null){
if(error.getResponseHeader("authorizeuser") == "UnAuthorized"){
result.State = 403;
}else{
result.State = 500;
}
}
callback(result);
}
});
}
$.ajax({
url: '/Login/TestUserAuthentication',
type: 'GET',
dataType: 'JSON',
headers: {"Authorization": localStorage.getItem('Token')},
success: function (data: ResultObject) {
callback(data);
},
error: function (error) {
let result = new ResultObject();
if(error.getResponseHeader("authorizeuser") != undefined || error.getResponseHeader("authorizeuser") != null){
if(error.getResponseHeader("authorizeuser") == "UnAuthorized"){
result.State = 403;
}else{
result.State = 500;
}
}
callback(result);
}
});
}
Happy Coding, Peace..
Great .
ReplyDelete