Understanding JWT: A Guide to Secure Authorization with Refresh token
Introduction
JSON Web Tokens (JWT) have become a cornerstone in modern web development for secure authorization and information exchange between parties.
In this blog post, we will delve into the fundamentals of JWT, explore the process of creating a secure authorization mechanism, and discuss the importance of token blacklisting for heightened security.
What is JWT?
JSON Web Token (JWT) is a compact, URL-safe means of representing claims between two parties. It is often used for authentication and authorization in web development. A JWT is composed of three parts: a header, a payload, and a signature. The header and payload are Base64-encoded JSON objects, and the signature is created by hashing the encoded header and payload with a secret key.
Before Creating a JWT Token you need to understand the Principles and Claims
Principles:
A principle in identity management represents an entity, often a user or a system, and encapsulates the entity's identity, authentication status, and any additional context. Principles are typically represented by objects such as ClaimsPrincipal in .NET or Principal in Java.
Claims:
Claims, on the other hand, are individual pieces of information about a principle. These can include attributes like a user's name, email, roles, or any other relevant data. Claims are statements about a principle, and they are often represented as key-value pairs.
When to Use Principles
1. Authentication:
Principles are particularly useful during the authentication process. When a user logs in, a principle is created to represent that authenticated user. This principle can then be used throughout the application to determine the user's identity.
2. Authorization:
In the context of authorization, principles play a vital role. Roles and permissions associated with a user are typically represented as claims within the principle. This allows for straightforward access control decisions based on the user's identity.
3. Contextual Information:
If you need to carry contextual information about the user throughout the application, principles are a suitable choice. This might include details such as the user's authentication method, authentication time, or any other metadata.
When to Use Claims
1. Fine-Grained Authorization:
Claims shine when you need to implement fine-grained authorization. By associating specific claims with a user, you can make nuanced decisions about what resources or actions they can access within your application.
2. User Profile Information:
Claims are ideal for carrying user profile information. Details like the user's full name, email address, or any custom attributes can be conveniently represented as claims.
3. Dynamic Permissions:
If your application requires dynamic permissions that may change based on runtime conditions, using claims to represent these dynamic aspects is a pragmatic approach.
Making the Right Choice
1. Consider Use Case:
When deciding between principles and claims, consider your application's use case. If you primarily need to represent a user's identity, use principles. If you require a more granular representation of attributes and permissions, opt for claims.
2. Combination Approach:
In many scenarios, a combination of principles and claims is the most effective. Use principles for identity and overarching context, and use claims to carry specific pieces of information about the user.
3. Flexibility and Maintainability:
Think about the future scalability and maintainability of your application. A well-thought-out combination of principles and claims can provide the flexibility needed as your application evolves.
Below is the code to Generate a token with the Refresh token
- [HttpPost]
- public IActionResult Post([FromBody] User value)
- {
- var user = Startup.Users
- .Find(x => x.UserName == value.UserName);
- if(user is null)
- {
- return StatusCode(StatusCodes.Status401Unauthorized, "No proper credentials");
- }
- if (user.Password==value.Password) // Check DB
- {
- MyToken tk = new MyToken();
- tk.TokenValue = GenerateJSONWebToken(user.UserName,30);
- tk.RefreshToken = GenerateRefreshToken();
- user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7);
- user.RefreshToken = tk.RefreshToken;
- return Ok(tk);
- }
- else
- {
- return StatusCode(StatusCodes.Status401Unauthorized , "No proper credentials");
- }
- }
- private ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
- {
- var tokenValidationParameters = new TokenValidationParameters
- {
- ValidateIssuer = true,
- ValidateAudience = true,
- ValidateLifetime = true,
- ValidateIssuerSigningKey = true,
- ValidIssuer = "lawson",
- ValidAudience = "BrowserClients",
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret@123456"))
- };
- var tokenHandler = new JwtSecurityTokenHandler();
- SecurityToken securityToken;
- var principal = tokenHandler.ValidateToken(token,
- tokenValidationParameters,
- out securityToken);
- var user = Startup.Users
- .Find(x => x.UserName == principal.Identity.Name);
- var jwtSecurityToken = securityToken as JwtSecurityToken;
- if (jwtSecurityToken == null
- || user.RefreshTokenExpiryTime <= DateTime.Now)
- throw new SecurityTokenException("Invalid token");
- return principal;
- }
- private string GenerateJSONWebToken(string username, int expiryTime)
- {
- // header info
- var algo = SecurityAlgorithms.HmacSha256;
- // payload info
- var claims = new[] {
- new Claim(ClaimTypes.Name, username),
- new Claim(JwtRegisteredClaimNames.Sub, username),
- new Claim(JwtRegisteredClaimNames.Email, ""),
- new Claim("IsAdmin", "True"),
- new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
- };
- // signature
- var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret@123456"));
- var credentials = new SigningCredentials(securityKey, algo);
- var token = new JwtSecurityToken("lawson",
- "BrowserClients",
- claims,
- expires: DateTime.Now.AddSeconds(expiryTime),
- signingCredentials: credentials);
- return new JwtSecurityTokenHandler().WriteToken(token);
- }
- private string GenerateRefreshToken()
- {
- var randomNumber = new byte[32];
- using (var rng = RandomNumberGenerator.Create())
- {
- rng.GetBytes(randomNumber);
- return Convert.ToBase64String(randomNumber);
- }
- }
Calling refresh token when the token is expired
- [HttpGet]
- public IActionResult Get(MyToken TokenVale)
- {
- var token = RefreshToken(TokenVale);
- return Ok(token);
- }
- private MyToken RefreshToken(MyToken tokenApiModel)
- {
- if (tokenApiModel is null)
- throw new Exception("Invalid client request");
- string accessToken = tokenApiModel.TokenValue;
- string refreshToken = tokenApiModel.RefreshToken;
- var principal = GetPrincipalFromExpiredToken(accessToken);
- var username = principal.Identity.Name; //this is mapped to the Name claim by default
- var user = Startup.Users.SingleOrDefault(u => u.UserName == username);
- if (user is null || user.RefreshToken != refreshToken)
- throw new Exception("Invalid client request");
- var newAccessToken = GenerateJSONWebToken(user.UserName, 60);
- var newRefreshToken = GenerateRefreshToken();
- user.RefreshToken = newRefreshToken;
- user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7);
- tokenApiModel.RefreshToken = newRefreshToken;
- tokenApiModel.TokenValue = newAccessToken;
- return tokenApiModel;
- }

Post a Comment
0Comments