Please review my code for 'JWT' authentication.
- Are there any security issues?
- Where should I store the secret's key, DB or InMemory?
- What's a good 'JWT' Lifetime?
- Should I send the 'JWT' in Header for every request?
- What should be done when a 'JWT' Expires?
Token Manager
using System;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
using Procoor_V4_Model.SecurityModel;
namespace Procoor_V4_Security
{
public class TokenManager
{
public static string EncodeToken(JwtPayload jwtPayload, string secret)
{
const string algorithm = "HS256";
var header = new JwtHeader
{
Typ = "JWT",
Alg = algorithm
};
var jwt = Base64Encode(JsonConvert.SerializeObject(header)) + "." + Base64Encode(JsonConvert.SerializeObject(jwtPayload));
jwt += "." + Sign(jwt, secret);
return jwt;
}
public static JwtPayload DecodeToken(string token, string secret)
{
var segments = token.Split('.');
if(segments.Length != 3)
throw new Exception("Token structure is incorrect!");
JwtHeader header = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Base64Decode(segments[0])), typeof(JwtHeader));
JwtPayload jwtPayload = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Base64Decode(segments[1])), typeof(JwtPayload));
var rawSignature = segments[0] + '.' + segments[1];
if(!Verify(rawSignature, secret, segments[2]))
throw new Exception("Verification Failed");
return jwtPayload;
}
private static bool Verify(string rawSignature, string secret, string signature)
{
return signature == Sign(rawSignature, secret);
}
private static string Sign(string str, string key)
{
var encoding = new ASCIIEncoding();
byte[] signature;
using (var crypto = new HMACSHA256(encoding.GetBytes(key)))
{
signature = crypto.ComputeHash(encoding.GetBytes(str));
}
return Base64Encode(signature);
}
public static string Base64Encode(dynamic obj)
{
Type strType = obj.GetType();
var base64EncodedValue = Convert.ToBase64String(strType.Name.ToLower() == "string" ? Encoding.UTF8.GetBytes(obj) : obj);
return base64EncodedValue;
}
public static dynamic Base64Decode(string str)
{
var base64DecodedValue = Convert.FromBase64String(str);
return base64DecodedValue;
}
}
}
How I currently use it:
var secret = TokenManager.Base64Encode(SecurityConstants.KeyForHmacSha256);
var currentTime = (long)(DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime()).TotalSeconds;
var payload = new JwtPayload
{
iss = SecurityConstants.TokenIssuer,
sub = "46897484",
iat = currentTime,
exp = currentTime + 1800
};
var jwt = TokenManager.CreateJwtToken(payload, secret);