Anatomy of a Simple Web Token (SWT)

Simple Web Token – name says it all. It is a token, it is for web (read HTTP) and it is simple! Then, there is good old SAML token, which is XML based. If there be light, then there is darkness; if cold, heat; if height, depth… If XML, JSON; so, there is a JSON web token (JWT) as well. SAML is more SOAP-ish and SWT and JWT are REST-ish.

Naturally, SWT is a good choice for ASP.NET Web API. Using OAuth 2.0, SWT can be sent in the HTTP authentication header (bearer scheme). That topic is too big for a single blog post. So, let’s focus on just SWT and look at using SWT as a bearer token through OAuth 2.0 hopefully in a future post. There is a great open source library for OAuth 2.0 – DotNetOpenAuth but my understanding, as of the time of writing this blog post is, SWT is not supported by DotNetOpenAuth for OAuth 2.0.

Anyways, let’s get on with dissecting a SWT. In fact, dissection will not be needed since what is inside a SWT is there for all to see. It is a simple token, after all! SWT is just HTML form encoded key value pairs. Exact keys and the corresponding values are left to be decided by the issuer and the relying party. However, there are a few keys that are mandatory for a token to be functional – Issuer, Audience and ExpiresOn. A token and hence the claims it contains has no face value if there is no issuer. If RBI Governor writes “I promise to pay the bearer sum of 1000 rupees” in a piece of paper and puts his signature below, it will buy things. If I do the same, it buys nothing. Issuer is important and so is the audience. For obvious reasons, tokens don’t live for ever and there is an expiry date. Apart from these three keys, there is one more key which is important and is even mandated by the SWT specification – HMACSHA256.

One of the fundamental properties of a token is that it should be tamper-proof. If anyone in the middle can intercept and change the claims or whatever is contained in the token, it is of no use. So, SWT is protected by HMAC. HMACSHA256 must be the last key in the token and corresponding value is the SHA 256 HMAC of the other key value pairs in the SWT. Issuer and the relying party to which token is minted for, share a secret 256-bit key. Since the key is shared i.e. same key is used at both ends, it is a symmetric key. Unlike a certificate which is a ‘ownership factor’, this key is a ‘knowledge factor’. Difficult to remember like a password, yet can be known whereas a certificate has to be possessed or owned. So, it is important to expire such keys after a time period and generate a new one.

HMACSHA256 key serves two purpose – one it makes the token tamper proof and two – it proves to the relying party that the token is authentic. Relying party can use the key in its possession and compute HMAC for all the key value pairs except HMACSHA256. If, HMAC thus computed, matches the value of the HMACSHA256 key, relying party can be confident that token was issued by an entity it trusts and that token is not changed in any way during the transit. HMAC ensures authenticity and the integrity but not confidentiality of token contents. Anyone will be able to look at the content of SWT. Sure enough, SWT can be encrypted but that is outside the scope of SWT specification.

Here is the TokenIssuer. GenerateKey() generates and returns a 256-bit key. Any practical issuer has to store this against the audience for later use but we don’t do that since this is just for illustration. GetToken() creates a new SWT, adds a few hard-coded claims, totally ignoring the credentials and sends back the string representation of the token.

public class TokenIssuer
{
    public string GenerateKey(string audience)
    {
        // 256-bit key - we generate and return the key here.
        // In practice, key has to be stored against the
        // audience passed in so that when the same
        // audience asks for a token subsequently, corresponding
        // key can be used.
        using (var provider = new RNGCryptoServiceProvider())
        {
            byte[] secretKeyBytes = new Byte[32];
            provider.GetBytes(secretKeyBytes);

            return Convert.ToBase64String(secretKeyBytes);
        }
    }

    public string GetToken(string audience, string credentials)
    {
        // Ignoring the credentials, let's add a few claims.
        // Instead of retrieving the shared key of the audience,
        // just hardcoding a key here. Both TokenIssuer and
        // RelyingParty has the same key.
        string key = "qqO5yXcbijtAdYmS2Otyzeze2XQedqy+Tp37wQ3sgTQ=";
        SimpleWebToken token = new SimpleWebToken(key)
                                     { Issuer = "TokenIssuer" };
        token.AddClaim(ClaimTypes.Name, "Badri");
        token.AddClaim(ClaimTypes.Email, "Badri@somewhere.in");
        token.AddClaim(ClaimTypes.Role, "Developer");
        token.AddClaim(ClaimTypes.Role, "Administrator");

        return token.ToString();
    }
}

RelyingParty accepts the token through PresentToken() and calls Parse(). If every thing goes well, token is created out of this string. Claims contained in the token are used to create a ClaimsIdentity and a ClaimsPrincipal, which is set to Thread.CurrentPrincipal (syntax can be slightly different, if you are on .NET 4.5). TheMethodRequiringAuthZ() uses PrincipalPermission and checks at a role, which is not as good as using ClaimsPermission but will be too much of info for a single blog entry to explain WIF CAM, etc.

public class RelyingParty
{
    // RelyingParty and TokenIssuer share the secret key (symmetric key)
    private string key = "qqO5yXcbijtAdYmS2Otyzeze2XQedqy+Tp37wQ3sgTQ=";

    public void PresentToken(string token)
    {
        try
        {
            SimpleWebToken swt = SimpleWebToken.Parse(token, key);
            Console.WriteLine(swt.ToString());

            // Now, swt.Claims will have the list of claims
            swt.Claims.ToList().ForEach(c =>
                Console.WriteLine("{0} ==> {1}", c.ClaimType, c.Value));

            Thread.CurrentPrincipal = new ClaimsPrincipal(new[]
                                        {
                                            new ClaimsIdentity(swt.Claims)
                                        });
        }
        catch (Exception ex)
        {
             Console.WriteLine(ex.Message);
        }
    }

    [PrincipalPermission(SecurityAction.Demand, Role = "Developer")]
    public void TheMethodRequiringAuthZ()
    {
        Console.WriteLine("Remember what uncle Ben said...");
        Console.WriteLine("With great power comes great responsibility");
    }
}

Here is the code that brings TokenIssuer and RelyingParty together. Relying party is just a term here to denote your application –  application that trusts a token issuer and works off the claims in the token.


static void Main(string[] args)
{
    // Token issuer
    TokenIssuer issuer = new TokenIssuer();

    // Relying party app
    RelyingParty app = new RelyingParty();

    // A client of the relying party app gets the token
    // Assume client knows magically it needs to get the token first
    // no WS-Federation or WS-Trust stuff
    string token = issuer.GetToken("MyRelyingPartApp", "opensesame");

    // With the token, client now presents the token and calls
    // the method requiring authorization
    app.PresentToken(token);
    app.TheMethodRequiringAuthZ();
}

Finally, the code for SimpleWebToken. I intentionally stayed away from Windows Identity Foundation (WIF) except for using Microsoft.Identity.dll (since I’m on .NET 4.0). TokenIssuer class above is not a sub-class of SecurityTokenService. SimpleWebToken will not inherit from SecurityToken either, just to keep the focus on SWT. Claims based identity, WIF, etc is an ocean and there are giants in that area – Dominick Baier, Vittorio Bertocci (Captain Identity) to name a few. There is quite a bit of information available from them and others.

SimpleWebToken has a bunch of properties – Issuer, Audience, ExpiresOn and Signature. ExpiresOn is not a time stamp but is the number of seconds since mid night of January 1, 1970 (UTC). This is called Unix time. Signature is just the value of HMACSHA256 key. Constructor takes in the 256-bit code shared key of the audience for which, this token is getting created.

public class SimpleWebToken
{
    private static readonly TimeSpan lifeTime = new TimeSpan(0, 2, 0);
    private static readonly DateTime epochStart = new DateTime
                          (1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
    private NameValueCollection keyValuePairs;
    private byte[] keyBytes = null;

    public SimpleWebToken(string key)
    {
        TimeSpan ts = DateTime.UtcNow - epochStart + lifeTime;
        this.ExpiresOn = Convert.ToUInt64(ts.TotalSeconds);
        this.keyValuePairs = new NameValueCollection();

        var securityKey = new InMemorySymmetricSecurityKey
                                     (Convert.FromBase64String(key));
        keyBytes = securityKey.GetSymmetricKey();
    }

    public string Issuer { get; set; }
    public string Audience { get; set; }
    public byte[] Signature { get; set; }
    public ulong ExpiresOn { get; private set; }
}

Now, let us get to the claims part. There is a method to add claims in the form of key value pairs. There is a read-only property that returns the claims thus added in the form of IList<Claim>. A little bit of complication here is that getter handles the case of multi-value claims such as role. There could be multiple claims with same key and in such cases, values for that key are grouped as comma separated values. So, role=role1&role=role2 will be role=role1,role2.

public IList<Claim> Claims
{
    get
    {
        return this.keyValuePairs.AllKeys
            .SelectMany(key =>
                this.keyValuePairs[key].Split(',')
                    .Select(value => new Claim(key, value)))
                       .ToList();
    }
}

public void AddClaim(string name, string value)
{
    this.keyValuePairs.Add(name, value);
}

ToString() is overridden to return the string representation of the SWT.

public override string ToString()
{
    StringBuilder content = new StringBuilder();

    content.Append("Issuer=").Append(this.Issuer);

    foreach (string key in this.keyValuePairs.AllKeys)
    {
        content.Append('&').Append(key)
            .Append('=').Append(this.keyValuePairs[key]);
    }

    content.Append("&ExpiresOn=").Append(this.ExpiresOn);

    if (!string.IsNullOrWhiteSpace(this.Audience))
    {
        content.Append("&Audience=").Append(this.Audience);
    }

    using (HMACSHA256 hmac = new HMACSHA256(keyBytes))
    {
        byte[] signatureBytes = hmac.ComputeHash(
            Encoding.ASCII.GetBytes(content.ToString()));

        string signature = HttpUtility.UrlEncode(
           Convert.ToBase64String(signatureBytes));

        content.Append("&HMACSHA256=").Append(signature);
    }

    return content.ToString();
}

Finally, the Parse() method, which creates back a SWT object from its string representation, while validating the signature and token expiration at the same time.

public static SimpleWebToken Parse(string token, string secretKey)
{
    var items = HttpUtility.ParseQueryString(token);
    var swt = new SimpleWebToken(secretKey);

    foreach (string key in items.AllKeys)
    {
        string item = items[key];
        switch (key)
        {
            case "Issuer": swt.Issuer = item; break;
            case "Audience": swt.Audience = item; break;
            case "ExpiresOn": swt.ExpiresOn = ulong.Parse(item); break;
            case "HMACSHA256": swt.Signature =
                                  Convert.FromBase64String(item); break;
            default: swt.AddClaim(key, items[key]); break;
        }
    }

    string rawToken = swt.ToString(); // Computes HMAC inside ToString()
    string computedSignature = HttpUtility.ParseQueryString(rawToken)
                                                       ["HMACSHA256"];

    if (!computedSignature.Equals(Convert.ToBase64String(swt.Signature),
                                                StringComparison.Ordinal))
        throw new SecurityTokenValidationException("Signature is invalid");

    TimeSpan ts = DateTime.UtcNow - epochStart;

    if (swt.ExpiresOn < Convert.ToUInt64(ts.TotalSeconds))
        throw new SecurityTokenException("Token has expired");

    return swt;
}
Advertisements

One thought on “Anatomy of a Simple Web Token (SWT)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s