Monday, October 9, 2017

Javascript Puzzle No.2 (beginner)

The Object.keys is supposed to return keys (property names) of given object. As the docs says, it should be equivalent of the for...in loop.

Is it really so? This little snippet shows that Object.keys doesn't really enumerate keys but rather ... values.

var p = {
    foo : 0,
    bar : 1
};

for ( var e in p ) {
    console.log( e );
}

for ( var e in Object.keys( p ) ) {
    console.log( e );
}
The snippet prints
foo
bar
0
1
while it definitely should print
foo
bar
foo
bar
What's wrong here?

Thursday, October 5, 2017

JWT tokens between .NET and node.js

Node.js JWT tokens are straightforward
var jwt = require('jsonwebtoken');

var secret = "This is my shared, not so secret, secret!";
var payload = {
    "unique_name":"myemail@myprovider.com",
    "role":"Administrator",
    "nbf":1499944099,
    "exp":1599947699,
    "iat":1499944099,
    "iss":"http://my.tokenissuer.com",
    "aud":"http://my.website.com"};

var token = jwt.sign(payload, secret);

console.log( token );

// -------------------------

var decoded = jwt.verify(token, secret);
console.log( decoded.unique_name );
thanks to the jsonwebtoken package.

In case of .NET, things are a little bit more complicated as the JWT is implemented in System.IdentityModel.Tokens.Jwt. This is unfortunate, as the 5.x.x version of this library has been reimplemented to rely on Microsoft.IdentityModel. Because of this, many older tutorials became deprecated and people tend to have different issues.

To have a working code, reference System.IdentityModel.Tokens.Jwt 5.1.4 or newer from Nuget and then

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var plainTextSecurityKey = "This is my shared, not so secret, secret!";

            var signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(plainTextSecurityKey));

            var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(signingKey,
                Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);

            // -------------------------

            var claimsIdentity = new ClaimsIdentity(new List()
            {
                new Claim(ClaimTypes.Name, "myemail@myprovider.com"),
                new Claim(ClaimTypes.Role, "Administrator"),
            }, "Custom");

            var securityTokenDescriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor()
            {
                Audience = "http://my.website.com",
                Issuer   = "http://my.tokenissuer.com",

                Subject            = claimsIdentity,                
                SigningCredentials = signingCredentials                              
            };

            var tokenHandler          = new JwtSecurityTokenHandler();
            var plainToken            = tokenHandler.CreateToken(securityTokenDescriptor);
            var signedAndEncodedToken = tokenHandler.WriteToken(plainToken);

            Console.WriteLine(plainToken);
            Console.WriteLine(signedAndEncodedToken);

            // -------------------------

            var tokenValidationParameters = new TokenValidationParameters()
            {
                ValidAudiences = new string[]
                {
                    "http://my.website.com",
                    "http://my.otherwebsite.com"
                },
                ValidIssuers = new string[]
                {
                    "http://my.tokenissuer.com",
                    "http://my.othertokenissuer.com"
                },
                IssuerSigningKey = signingKey                
            };

            Microsoft.IdentityModel.Tokens.SecurityToken validatedToken;
            var validatedPrincipal = tokenHandler.ValidateToken(signedAndEncodedToken,
                tokenValidationParameters, out validatedToken);

            Console.WriteLine(validatedPrincipal.Identity.Name);
            Console.WriteLine(validatedToken.ToString());

            Console.ReadLine();
        }
    }
}