Web API 2 authentication with JWT Please review my code for bearer token (JWT) authentication of Web API 2 (Self Hosted using OWIN)

Are there any security issues in the implementation?

Quick overview:


Token creation and validation using JWT Handler
Symmetric key encryption
CORS support not yet checked for the authorization header
Web traffic will be on SSL.
The key cannot be auto-generated as it will break during a load balanced scenario. Can I save the key in config? Or switch to X509 certificates?


This is the main class to create and validate tokens:

public class TokenManager
{
    public static string CreateJwtToken(string userName, string role)
    {
        var claimList = new List&lt;Claim&gt;()
            {
                new Claim(ClaimTypes.Name, userName),
                new Claim(ClaimTypes.Role, role)     //Not sure what this is for
            };

        var tokenHandler = new JwtSecurityTokenHandler() { RequireExpirationTime = true };
        var sSKey = new InMemorySymmetricSecurityKey(SecurityConstants.KeyForHmacSha256);

        var jwtToken = tokenHandler.CreateToken(
            makeSecurityTokenDescriptor(sSKey, claimList));
        return tokenHandler.WriteToken(jwtToken);
    }

    public static ClaimsPrincipal ValidateJwtToken(string jwtToken)
    {
        var tokenHandler = new JwtSecurityTokenHandler() { RequireExpirationTime = true };

        // Parse JWT from the Base64UrlEncoded wire form 
        //(&lt;Base64UrlEncoded header&gt;.&lt;Base64UrlEncoded body&gt;.&lt;signature&gt;)
        JwtSecurityToken parsedJwt = tokenHandler.ReadToken(jwtToken) as JwtSecurityToken;

        TokenValidationParameters validationParams =
            new TokenValidationParameters()
            {
                AllowedAudience = SecurityConstants.TokenAudience,
                ValidIssuer = SecurityConstants.TokenIssuer,
                ValidateIssuer = true,
                SigningToken = new BinarySecretSecurityToken(SecurityConstants.KeyForHmacSha256),
            };

        return tokenHandler.ValidateToken(parsedJwt, validationParams);
    }

    private static SecurityTokenDescriptor makeSecurityTokenDescriptor(
        InMemorySymmetricSecurityKey sSKey, List&lt;Claim&gt; claimList)
    {
        var now = DateTime.UtcNow;
        Claim[] claims = claimList.ToArray();
        return new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            TokenIssuerName = SecurityConstants.TokenIssuer,
            AppliesToAddress = SecurityConstants.TokenAudience,
            Lifetime = new Lifetime(now, now.AddMinutes(SecurityConstants.TokenLifetimeMinutes)),
            SigningCredentials = new SigningCredentials(sSKey,
                "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
                "http://www.w3.org/2001/04/xmlenc#sha256"),
        };
    }
}


I have a message handler to intercept the requests and I verify the validity of token except for the route for log in, using TokenManager.ValidateJwtToken() above.

To create the token, in the LoginController, I have the following code:

[Route("login")]
[HttpPost]
public HttpResponseMessage Login(LoginBindingModel login)
{
    if (login.Username == "admin" &amp;&amp; login.Password == "password")  //Do real auth
    {
        string role = "Librarian";
        var jwtToken = TokenManager.CreateJwtToken(login.Username, role);

        return new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new ObjectContent&lt;object&gt;(new
            {
                UserName = login.Username,
                Roles = role,
                AccessToken = jwtToken
            }, Configuration.Formatters.JsonFormatter)
        };
    }
    return new HttpResponseMessage(HttpStatusCode.BadRequest);
}


The full working code is available here and the instructions to run the sample are in the Wiki.
Instead of keeping KEY in config, I would keep it with user records. A unique key for each user.

I admit, I don't get why creating keys dynamically would break the load balancing scenario.  We can have a key created at the back-end where we have a single service serving all the load balances servers (such as a database).