A Comprehensive Guide to Generating and Verifying JWT Tokens in C
JSON Web Tokens (JWT) have become a popular method for securing web applications and APIs due to their simplicity and effectiveness. In this comprehensive guide, we will explore how to generate and verify JWT tokens in C#, ensuring the security and integrity of your applications.
What is a JWT Token?
A JWT token is a compact, URL-safe string that represents claims between two parties. It consists of three parts: a header, a payload, and a signature. The header contains metadata about the token, such as the signing algorithm. The payload contains the claims or information about the user. The signature ensures the token’s authenticity and integrity.
Generating a JWT Token
To generate a JWT token in C#, we will follow these steps:
- Create the Header and Payload as JSON objects.
- Base64URL encode the Header and Payload separately.
- Concatenate the encoded Header and Payload with a dot (’.’) separator.
- Sign the token by encoding the concatenated Header and Payload with a secret key using the HMACSHA256 algorithm.
- Base64URL encode the signature.
- Concatenate the encoded Header, Payload, and Signature with dots (’.’) to form the final JWT token.
Let’s illustrate this process with a Mermaid diagram:
Step-by-Step Guide to Generating JWT Tokens in C
Step 1: Create the Header
The header specifies the signing algorithm and token type. Use the provided code snippet to create the header using the alg (algorithm) and typ (token type) values.
var header = new { alg = "HS256", typ = "JWT" };
var encodedHeader = Base64UrlEncode(Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(header)));
Step 2: Create the Payload
The payload contains the claims or information about the user. Customize the payload based on your application requirements.
var payload = new { userId = 123, username = "john_doe", roles = new[] { "admin", "user" } };
var encodedPayload = Base64UrlEncode(Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(payload)));
Step 3: Generate the Secret Key
Generate a secret key to sign the token. The secret key should be kept confidential and stored securely.
Step 4: Create the Signature
The signature ensures the integrity of the token. Sign the token by encoding the concatenated header and payload with the secret key using the HMACSHA256 algorithm.
var signature = SignToken($"{encodedHeader}.{encodedPayload}", secretKeyBytes);
var encodedSignature = Base64UrlEncode(signature);
Step 5: Build the JWT Token
Combine the encoded header, payload, and signature with dots to form the final JWT token.
var jwtToken = $"{encodedHeader}.{encodedPayload}.{encodedSignature}";
Complete Code Example
Here’s a complete code example that demonstrates the generation of a JWT token:
public class JwtTokenGenerator
{
public static string GenerateJwtToken(object payload, string secretKey)
{
var header = new { alg = "HS256", typ = "JWT" };
var encodedHeader = Base64UrlEncode(Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(header)));
var payloadJson = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
var encodedPayload = Base64UrlEncode(Encoding.UTF8.GetBytes(payloadJson));
var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
var signature = SignToken($"{encodedHeader}.{encodedPayload}", secretKeyBytes);
var encodedSignature = Base64UrlEncode(signature);
var jwtToken = $"{encodedHeader}.{encodedPayload}.{encodedSignature}";
return jwtToken;
}
private static byte[] SignToken(string input, byte[] secretKey)
{
using (var hmac = new HMACSHA256(secretKey))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(input));
}
}
private static string Base64UrlEncode(byte[] input)
{
var base64 = Convert.ToBase64String(input);
var base64Url = base64.Replace("+", "-").Replace("/", "_").TrimEnd('=');
return base64Url;
}
}
public class Program
{
public static void Main(string[] args)
{
var payload = new
{
userId = 123,
username = "john_doe",
roles = new[] { "admin", "user" }
};
var secretKey = "YourSecretKey";
var jwtToken = JwtTokenGenerator.GenerateJwtToken(payload, secretKey);
Console.WriteLine(jwtToken);
}
}
Ensure that you replace “YourSecretKey” with an actual secret key for secure token generation.
Token Validation Process
To validate a JWT token, we need to perform the following steps:
- Decode the header and payload from the token.
- Verify the token signature.
- Check the token expiration (if applicable).
Let’s examine each step in more detail.
Step 1: Decode the Header and Payload
The first step in token validation is to decode the header and payload from the JWT token. This involves splitting the token into its constituent parts, decoding the Base64URL-encoded header and payload, and converting them from bytes to strings.
private static byte[] Base64UrlDecode(string input)
{
var base64Url = input.Replace("-", "+").Replace("_", "/");
switch (base64Url.Length % 4)
{
case 2: base64Url += "=="; break;
case 3: base64Url += "="; break;
}
return Convert.FromBase64String(base64Url);
}
public static bool ValidateJwtToken(string jwtToken)
{
var tokenParts = jwtToken.Split('.');
var encodedHeader = tokenParts[0];
var encodedPayload = tokenParts[1];
var header = Encoding.UTF8.GetString(Base64UrlDecode(encodedHeader));
var payload = Encoding.UTF8.GetString(Base64UrlDecode(encodedPayload));
// TODO: Continue with the remaining validation steps
}
Step 2: Verify the Token Signature
Next, we need to verify the token signature to ensure that it hasn’t been tampered with. This requires extracting the signature from the token, reconstructing the input string (concatenation of the encoded header and payload), and computing the hash using the same HMACSHA256 algorithm used during token generation.
private static bool VerifyTokenSignature(string input, byte[] signature, byte[] secretKey)
{
using (var hmac = new HMACSHA256(secretKey))
{
var computedSignature = hmac.ComputeHash(Encoding.UTF8.GetBytes(input));
return computedSignature.SequenceEqual(signature);
}
}
public static bool ValidateJwtToken(string jwtToken, string secretKey)
{
// Previous code for decoding the header and payload
var signature = Base64UrlDecode(tokenParts[2]);
var input = $"{encodedHeader}.{encodedPayload}";
var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
var isValidSignature = VerifyTokenSignature(input, signature, secretKeyBytes);
// TODO: Continue with the remaining validation steps
}
Step 3: Check Token Expiration
In some cases, JWT tokens include an expiration time (exp) claim. We should check if the token has expired by comparing the expiration time with the current time. If the token has expired, it should be considered invalid.
private static bool CheckTokenExpiration(string payload)
{
var payloadJson = JsonConvert.DeserializeObject<dynamic>(payload);
var expiration = payloadJson.exp;
if (expiration == null)
{
// Expiration claim does not exist
return false;
}
var expirationDateTime = DateTimeOffset.FromUnixTimeSeconds((long)expiration);
return DateTimeOffset.UtcNow > expirationDateTime;
}
public static bool ValidateJwtToken(string jwtToken, string secretKey)
{
// Previous code for decoding the header and payload
var isValidSignature = VerifyTokenSignature(input, signature, secretKeyBytes);
var isTokenExpired = CheckTokenExpiration(payload);
return isValidSignature && !isTokenExpired;
}
Putting It All Together
Now that we have the complete process for validating JWT tokens, let’s integrate it into our existing code.
public class JwtTokenValidator
{
public static bool ValidateJwtToken(string jwtToken, string secretKey)
{
var tokenParts = jwtToken.Split('.');
// Step 1: Decode the Header and Payload
var encodedHeader = tokenParts[0];
var encodedPayload = tokenParts[1];
var header = Encoding.UTF8.GetString(Base64UrlDecode(encodedHeader));
var payload = Encoding.UTF8.GetString(Base64UrlDecode(encodedPayload));
// Step 2: Verify the Signature
var signature = Base64UrlDecode(tokenParts[2]);
var input = $"{encodedHeader}.{encodedPayload}";
var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
var isValidSignature = VerifyTokenSignature(input, signature, secretKeyBytes);
// Step 3: Check Token Expiration (if applicable)
var isTokenExpired = CheckTokenExpiration(payload);
// Step 4: Return the Token Validation Result
return isValidSignature && !isTokenExpired;
}
private static bool VerifyTokenSignature(string input, byte[] signature, byte[] secretKey)
{
using (var hmac = new HMACSHA256(secretKey))
{
var computedSignature = hmac.ComputeHash(Encoding.UTF8.GetBytes(input));
return computedSignature.SequenceEqual(signature);
}
}
private static bool CheckTokenExpiration(string payload)
{
var payloadJson = JsonConvert.DeserializeObject<dynamic>(payload);
var expiration = payloadJson.exp;
if (expiration == null)
{
// Expiration claim does not exist
return false;
}
var expirationDateTime = DateTimeOffset.FromUnixTimeSeconds((long)expiration);
return DateTimeOffset.UtcNow > expirationDateTime;
}
private static byte[] Base64UrlDecode(string input)
{
var base64Url = input.Replace("-", "+").Replace("_", "/");
switch (base64Url.Length % 4)
{
case 2: base64Url += "=="; break;
case 3: base64Url += "="; break;
}
return Convert.FromBase64String(base64Url);
}
}
public class Program
{
public static void Main(string[] args)
{
var jwtToken="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywidXNlcm5hbWUiOiJqb2huX2RvZSIsInJvbGVzIjpbImFkbWluIiwidXNlciJdfQ.VHniBzdbfWcNb6YxgcGzFJ6VQLmkTcSCgHN3Leqq-GI";
var secretKey = "YourSecretKey";
var isValid = JwtTokenValidator.ValidateJwtToken(jwtToken, secretKey);
Console.WriteLine($"Token is valid: {isValid}");
}
}
In this blog post, we explored the process of generating JWT tokens in C# using a step-by-step guide. We discussed the components of JWT tokens, including the header, payload, and signature, and their significance in securing web applications. By following the provided code examples and understanding the token generation process, developers can implement JWT token generation in their own applications, enhancing security and enabling secure authentication and authorization.