Poor Man’s Delegation: Web API Version 2, CORS and System.IdentityModel.Tokens.Jwt Part 1   3 comments

Deeply Disturbing Technical Background

Microsoft calls the assembly System.IdentityModel.Tokens.Jwt:  .Net 4.5 support for JSON Web Security Tokens.  The OAuth Working group Draft  can be found here.  The Working group helpfully suggests that JWT be pronounced as the English word “jot” but we just say “J W T” around our shop. So what is it good for and why would I want to create and consume one?  Often call Poor Man’s Delegation, the JWT is a convenient way for heterogeneous services to communicate claim validity to each other in other for services to be consumed across domain boundaries.  We first heard the term Poor Man’s Delegation in discussion with Brock Allen whose blog we strongly recommend for anyone interested in modern internet security from a .Net perspective. While we are plugging you could do worse than to check out the man who knows more about .Net 4.5 security than anyone not under NDA:  Dominick Baier.  Vittorio Beertocci gives an overview here, with the mandatory confusing and scary diagrams. His introduction to preview of System.IdentityModel.Tokens.Jwt is given here (but note some of the names have changed since this 11/2012 blog was posted).  Please check out his Vittorio’s blog and links to get a feel for the topic.  I will not be writing a tutorial here but will be looking at some cook book approaches (not based on Azure and not using an external STS) in this post.  You must obtain the System.IdentityModel.Tokens.Jwt assembly as a NuGet package here.  Some additional reference links can be found here and here.

Vittorio Beertocci’s view!

What We Would Like To Do

Here at Dog Patch Computing we have a very big commercial software system which we call The Monster (it rhymes with “spare part”) which controls our lives.  The security internals of The Monster are obscure and control by our corporate masters far far away in another part of the galaxy.  We develop primarily SPA (single page applications) intended to be hosted on phones and other devices.  Most of our data resides on servers which we control which are not part of the monster.  We must, must, must authenticate our users in The Monster but we need users to access their data via AJAX services running on servers which we control but which are not part of The Monster. So our situation looks like this:

image

We could develop a “trusted relation” between these two systems and “flow” The Monster’s credentials to our local machines.  While technically feasible the details of implementing this are quite complex and frankly we like light weight solutions for simple problems. The Monster handles authentication and holds critical information about each user including the user’s roles and identifiers used to associate the user with data to which she should have access to.  Our datamart holds the data the user wants access to.  What we want is a simple light way for the client device to access the datamart using AJAX calls and have access only to the data they are authorized to see.  We don’t want the datamart to be an authentication server or to maintain a user database replicating information held by The Monster .  We want the AJAX calls to be secure.

When a user authenticates to The Monster we exploit a hook which allows us to generate a JWT object unique to that user with the claims associated with that user which are relevant to their data on our servers.  We use System.IdentityModel.Tokens.Jwt to do this.  This object is encrypted. The JWT object is passed to the device browser.  When the device needs data from our datamart servers the JWT object is passed in a authorization header attached to the AJAX call. Note that this is a cross server (CORS) call. I will cover CORS processing in Web API in part 2 of this post. 

The Sender must preform the following tasks:

1.  Authenticate the user

2. Associate the user with Roles and Claims

3. Create A signed (encrypted) properly formatted JWT object

4. Return the JWT to the calling device.

The receiver of the AJAX call must do the following tasks:

  1. Decrypt the JWT and Authenticate the AJAX caller,
  2. Process the CORS request correctly,
  3. Create a Federated principle,
  4. Assign this principle to the current thread
  5. Process the data request based on the Claims associated with the caller. 

Most of these details are handled easily with Web API version 2.  Specifically,

  1. Authorize the caller (based on the JWT):  System.IdentityModel.Tokens.Jwt, Web API 2 Route authorization Handler
  2. Process the CORS request correctly: customization of the CORS attribute (the CORS attribute was contributed by Brock Allen)
  3. Create a Federated principle, (Framework 4.5 BCL)
  4. Assign this principle to the current thread (Framework 4.5 BCL)
  5. Process the data request based on the Role Claim and other user specific Claims associated with the caller (Framework 4.5 BCL) 

Ok, let’s get out the cook book and do some cookin’.

Recipe for Creating A Signed JWT

Ingredients:

  • A List of claims.  In our kitchen this includes
    • Roles
    • User Name
    • Other Claims like data access keys
      • For example A claim might be Bank Account and the value of the claim is the Bank Account Number.
  • Issuer UNI (you can make this up)
  • Allowed Audience UNI (you can make this up)
  • Lifetime (this determines how long this JWT is valid)
  • Signing Credentials (more On this one later)

Issuer UNI: this is the FROM UNI which you agree to accept the JWT from.  This should take the form of (but could be any text string):

http://{THEMONSTERDOMAIN}

Allowed Audience UNI: this is the TO UNI which you identify yourself as the correct recipient.  This should take the form of (but could be any text string):

http://{MYDATASERVERDOMAIN}/

Lifetime: this is the start and stop valid date time of the JWT you are issuing. This takes the form of:

new System.IdentityModel.Protocols.WSTrust.Lifetime(

     now.ToUniversalTime(),

      now.AddMinutes({local parameter length of the lifetime})

);

Working With Claims

Claims (not clams): these are specified in key/value pairs. Where the Key is a text string UNI and the value is anything string you want.  Some UNI’s are already in general use. See System.Security.Claims.ClaimTypes for the complete list used by Microsoft.  Since we are interacting with a Microsoft Windows system we will use “http://schemas.microsoft.com/ws/2008/06/identity/claims/role as the Key for all of our defined Roles. For the user identifier we will use “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier” (this seems to be what ADFS is using.  For arbitrary claims we are creating our own uni keys.  In our case our application specific Claim is called AccountNumber and we created an UNI Key of:

http://{MYDATASERVERDOMAIN/Account

We can define more than one claim per ID.  That is, for example we can create multiple Role claims for a given JWT.  More formally, claims are of type

List<string,string> and NOT Dictionary<string,string>

In C# we create a single claims as:

var myClaim1=new Claim(ClaimTypes.Role, “Customer”);

var myClaim2=new Claim(“http://{MYDATASERVERDOMAIN/Account”, “12345678”);

and our array of claims as:

List claimLst = new List();

and

claimLst.Add(myClaim2)

add claims to our list and then make a claims array as:

System.Security.Claims.Claim[] claims = claimList.ToArray();

Ok so far?  Hold on to this idea and turn to the scary topic of:

Encryption and System.IdentityModel.Tokens.SigningCredentials

How paranoid are you? How paranoid do you need to be? The “SigningCredentials” for a JWT are the basis for encrypting the JWT.  The ability to decrypt the JWT requires knowledge of the “SigningCredentials” used by the call.  The sender and receiver must share a  cryptographic key (and other data) in order to exchange JWT objects securely.  In our case our JWT are time limited and contain private (but not secrete ) information.  So our paranoia is limited to: The JWT must be difficult to crack during the existing Lifetime of the JWT and difficult to counterfeit.  No encryption method is perfect, the Chinese (not to mention NSA) can given enough interest and time crack and counterfeit any object. Having said that we adopted a safe’ish SHA-256 encryption algorithm.  We generated our shared key using a Framework Cryptography Class.

Given a key called Key we can create a “SigningCredential” as:

new System.IdentityModel.Tokens.SigningCredentials(

              new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(Key),

             http://www.w3.org/2001/04/xmldsig-more#hmac-sha256,

              http://www.w3.org/2001/04/xmlenc#sha256

)

Combine Ingredients and Cook up a JWT

Ok, now that we have gotten our ingredients together let’s finally create a JWT object:

Create a Security Token Descriptor:

static System.IdentityModel.Tokens.SecurityTokenDescriptor _MakeSecurityTokenDescriptor                                        (System.IdentityModel.Tokens.InMemorySymmetricSecurityKey sSKey, List claimList)
{
var now = DateTime.UtcNow;
            System.Security.Claims.Claim[] claims = claimList.ToArray();
return new System.IdentityModel.Tokens.SecurityTokenDescriptor
{
Subject = new System.Security.Claims.ClaimsIdentity(claims),
TokenIssuerName = Constants.ValidIssuer,
AppliesToAddress = Constants.AllowedAudience,
Lifetime =
new System.IdentityModel.Protocols.WSTrust.Lifetime(now.ToUniversalTime(),

                      now.AddMinutes (AIC.MyBook2.Constants.JWT.LifeSpan),
SigningCredentials = new System.IdentityModel.Tokens.SigningCredentials(

                      sSKey,

                     http://www.w3.org/2001/04/xmldsig-more#hmac-sha256,

                      “http://www.w3.org/2001/04/xmlenc#sha256″),
};
}

SigningCredentials, AppliesToAddress and TokenIssueName MUST be shared between the sender and the receiver.  Lifetime determines how long the JWT object is valid for use.

Create the JWT Object (finally):

var tokenHandler = new System.IdentityModel.Tokens.JwtSecurityTokenHandler();
tokenHandler.RequireExpirationTime = true; //make that Lifetime mandatory
var myJWT=tokenHandler.WriteToken(tokenHandler.CreateToken(_MakeSecurityTokenDescriptor(sSKey, claimLst)));

Easy and fun ( and 64bit encoded for safe internet transfer).

Part II will cover validating and using the JWT on the receiver.

 

3 responses to “Poor Man’s Delegation: Web API Version 2, CORS and System.IdentityModel.Tokens.Jwt Part 1

Subscribe to comments with RSS.

  1. Pingback: Poor Man’s Delegation: Web API Version 2, CORS and System.IdentityModel.Tokens.Jwt Part 2 | Cloud2013 Or Bust

  2. Hey there, thank you for the write up, but it has left me a little confused :/

    None of this code (as far as I can see/understand) will encrypt thetokens? What we’re configuring up above is how to *sign* your token (so you can state reasonably safely that the contents of the token was not tampered with [as the attacker would need to also forge the signature]

    For anyone else who comes this way it might be sensible to note that you must not send these tokens on any non-https medium as they will be easily re-played by an attacker :/ (Or have I missed something crucial?)

    • These tokens discussed here are intended to be fully encrypted (usually with a symmetrical key). We recommend SSL transport security as a second layer of protection to avoid man in the middle attacks.

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

Follow

Get every new post delivered to your Inbox.

Join 117 other followers

%d bloggers like this: