By now we should understand the structure and process of how JWT Tokens works. Today in our example of user authentication in ASP.NET API 2 we will deal with AuthService, which is responsible for creating, signing and verifying JWT tokens.
In the previous part we covered MembershipProvider
(which downloads claims and validates the user) and RSAKeyProvider
(which provides the RSA key to encrypt/decrypt our JWT token).
AuthService
AuthService
is the most important element of our application. It’s responsible for creating JWT tokens as well as for signing and verifying an incoming ones. It has two methods (GenerateJwtToken()
and ValidateToken()
) and it uses both MembershipProvider
(login validation, downloading claims) and RSAKeyProvider
(for providing the signing key).
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Zaven.Practices.Auth.JWT.Providers.Interfaces;
using Zaven.Practices.Auth.JWT.Services.Interfaces;
using Microsoft.IdentityModel.Tokens;
...
public class AuthService : IAuthService
{
...
private readonly IMembershipProvider _membershipProvider;
private readonly IRSAKeyProvider _rsaProvider;
public AuthService(IMembershipProvider membershipProvider, IRSAKeyProvider rsaProvider)
{
_membershipProvider = membershipProvider;
_rsaProvider = rsaProvider;
}
…
}
Generating the JWT Token
Here is one of the two main AuthService
methods:
public async Task GenerateJwtTokenAsync(string username, string password)
{
if (!_membershipProvider.VerifyUserPassword(username, password))
return "Wrong access";
List claims = _membershipProvider.GetUserClaims(username);
string publicAndPrivateKey = await _rsaProvider.GetPrivateAndPublicKeyAsync();
if(publicAndPrivateKey == null)
return "RSA key error";
RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider();
publicAndPrivate.FromXmlString(publicAndPrivateKey);
JwtSecurityToken jwtToken = new JwtSecurityToken
(
issuer: "http://issuer.com",
audience: "http://mysite.com",
claims: claims,
signingCredentials: new SigningCredentials(new RsaSecurityKey(publicAndPrivate), SecurityAlgorithms.RsaSha256Signature),
expires: DateTime.Now.AddDays(30)
);
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
string tokenString = tokenHandler.WriteToken(jwtToken);
return tokenString;
}
VerifyUserPassword()
(part of MembershipProvider
) valides data provided by the user. If he doesn’t exist, a feedback message gets send. If the user exists, his claims are downloaded to be placed in the token later.
Next, we create the RSA key, which is delivered through RSAKeyProvider
by the GetPrivateAndPublicKey()
method. In this example, the key is being kept in a regular file. Note: in real-case scenarios keys should be kept in a safe place!
The object of the initiated RSACryptoServiceProvider
class is a previously created key (publicAndPrivateKey
).
RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider();
publicAndPrivate.FromXmlString(publicAndPrivateKey);
When we have all data, we can create the JWT token (in JSON format) as an object of JwtSecurityToken
class with following properties:
- issuer: the token sender
- audience: defines the token receiver
- claims: defines claims
- expires: defines the expiration date of a token
- signingCredentials: defines the method of data validation
Next, our JWT token has to be converted from JSON into a compact string (a character string separated by dots: xxx.yyy.zzz
). To do this, create an object of JwtSecurityTokenHandler
class and call it with WriteToken()
.
A such prepared Token is then sent to the user.
JWT Token validation
The second most important method of AuthService
is ValidateTokenAsync()
, which tries to decrypt a sent JWT token and checks its validity. If one of these two operations does not succeed, a Boolean status gets returned.
public async Task ValidateTokenAsync(string TokenString)
{
Boolean result = false;
try
{
SecurityToken securityToken = new JwtSecurityToken(TokenString);
JwtSecurityTokenHandler securityTokenHandler = new JwtSecurityTokenHandler();
RSACryptoServiceProvider publicAndPrivate = new RSACryptoServiceProvider();
string publicAndPrivateKey = await _rsaProvider.GetPrivateAndPublicKeyAsync();
if (publicAndPrivateKey == null)
return result;
publicAndPrivate.FromXmlString(publicAndPrivateKey);
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidIssuer = "http://issuer.com",
ValidAudience = "http://mysite.com",
IssuerSigningKey = new RsaSecurityKey(publicAndPrivate)
};
ClaimsPrincipal claimsPrincipal = securityTokenHandler.ValidateToken(TokenString, validationParameters, out securityToken);
result = true;
}
catch (Exception ex)
{
result = false;
}
return result;
}
Similarly, when signing, we create an object of type RSACryptoServiceProvider
that is initiated by a key stored in a global variable. Our token in the format of a compact string has to be converted back to JSON.
A TokenValidationParameters
object includes a list of parameters which will be used by a SecurityTokenHandler
object during the attempt of reading the JWT token. In this case the given properties are: issuer, audience and an object of RsaSecurityKey
which is responsible for decrypting.
ValidateToken()
from JwtsecurityTokenHandler
will attempt to read any claims. If the token is invalid or claims are unreadable, the exception will be caught and the method will return false.
In the next part, we can move on to programming the login controller and test our application.
Sources:
Popular posts
From Hype to Hard Hats: Practical Use Cases for AI chatbots in Construction and Proptech.
Remember the multimedia craze in the early 2000s? It was everywhere, but did it truly revolutionize our lives? Probably not. Today, it feels like every piece of software is labeled "AI-powered." It's easy to dismiss AI chatbots in construction as just another tech fad.
Read moreFears surrounding external support. How to address concerns about outsourcing software development?
Whether you’ve had bad experiences in the past or no experience at all, there will always be fears underlying your decision to outsource software development.
Read moreWhat do you actually seek from external support? Identify what’s preventing you from completing a project on time and within budget
Let’s make it clear: if the capabilities are there, a project is best delivered internally. Sometimes, however, we are missing certain capabilities that are required to deliver said project in a realistic timeline. These may be related to skills (e.g. technical expertise, domain experience), budget (hiring locally is too expensive) or just capacity (not enough manpower). What are good reasons for outsourcing software development?
Read more