Getting Access Control Service Tokens via HTTP

Some people like to say that French is the language of love. Others - mainly those with more interest in slipping you a network stack than an afrodisiac - will tell you that HTTP is the protocol equivalent. The AppFabric Service Bus loves HTTP too, and it also loves Security: in this post, I'm going to show you how to get - and understand - a security token from the Access Control Service via HTTP.

Claims Based Auth 101

To gain access to the service bus, you need to present a WRAP Access Token [PDF] in the HTTP Authorization Header for each of your calls. The content for that header is acquired from the access control service by presenting an assertion (like a signed Simple Web Token or a Username and Password pair) as a simple HTTP request; the response contains a set of claims (like who you are, where the token came from, how long the token lasts and which permissions the bearer has) which are signed to ensure authenticity.

Before we get started, you'll want to head over to the AppFabric Labs Portal. If you aren't signed up, take a moment to create a free account - once you're in click on the Service Bus node on the left and click the 'View' button on the right marked 'Default Issuer'. Note down the details (Issuer Name/Key) from here because you'll need them later.

Service Namespaces, Issers and Keys

When you create a new Service Bus Namespace, a default identity is established in the namespace's associated Access Control Service namespace. This identity has both Shared Secret and Password credentials available - you can see those credentials by logging in to Access Control Management at https:// [Your-Service-Namespace] -sb.accesscontrol.appfabriclabs.com/v2/mgmt/web/ServiceIdentity and clicking on 'owner' ('owner' is the name of the default service identity created when your account was provisioned). When your account is provisioned, the same key is used for both the Issuer Password credential and the Issuer Secret Key credential - this won't always be true as you create more issuers, passwords and secret keys: you don't need to have both a password and a secret key for an issuer, and if you do have both they don't need to be the same.

 string serviceNamespace = "contoso";
string issuerName  = "owner";
string issuerPassword = "<Enter Your Key Here>";

You can request a token from ACS using either the password or the shared secret - here is how they differ:

  1. A Username and Password are presented to ACS as credentials, if they match the stored values, an auth. token is returned.
  2. A Shared Secret is used to sign a set of claims which are presented to ACS. If the signature is valid for the presented claims, an auth. token is returned.

Regardless of how you request a token, you'll need to use the WRAPv0.9 ACS Endpoint for your Namespace (note the '-sb' suffix attached to the service namespace) and the Uri for your Service Bus service namespace using the https:// scheme (even though, for security purposes, the service bus is only accessible over https, tokens must be requested for http).

 string acsEndpoint = "https://" + serviceNamespace + "-sb.accesscontrol.appfabriclabs.com/WRAPv0.9/"; 
string relyingPartyAddress = "https://" + serviceNamespace + ".servicebus.appfabriclabs.com/";

Method 1: Acquiring a Token with a Username and Password Getting a token using a username and password is really simple - just post a collection of values to the ACS Endpoint including the Relying Party Address ('wrap_scope'), the Issuer Name ('wrap_name') and the Issuer Password ('wrap_password'). The collection of response variables includes the token (but we'll get to that later!):

 NameValueCollection postData = new NameValueCollection
    {
        { "wrap_scope", relyingPartyAddress },
        { "wrap_name", issuerName },
        { "wrap_password", issuerPassword },
    };

WebClient webClient = new WebClient();
byte[] responseBuffer = webClient.UploadValues(acsEndpoint, "POST", postData);
string response = Encoding.UTF8.GetString(responseBuffer);

Method 2: Acquiring a Token with a Shared Secret

The process with a Shared Secret is similar but has another step - we'll still post the Relying Party Address ('wrap_scope'), but instead of posting a username and password we'll send a Signed Simple Web Token ('wrap_assertion') along with a Token Format Identifier ('wrap_assertion_format', equal to 'SWT'). The token is simply a string containing the Issuer Name (in the format, Issuer={0}) signed with a HMACSHA256:

 
byte[] issuerSecretBytes = Convert.FromBase64String(issuerSecret);
string token = "Issuer=" + Uri.EscapeDataString(issuerName);
string signature;

using (HMACSHA256 sha256 = new HMACSHA256(issuerSecretBytes))
{
    byte[] signatureBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(token));
    signature = Convert.ToBase64String(signatureBytes);
}

string signedToken = token + "&HMACSHA256=" + Uri.EscapeDataString(signature);

With the signed token computed, we construct a collection of post data and send it as before:

 NameValueCollection postData = new NameValueCollection
    {
        { "wrap_scope", relyingPartyAddress },
        { "wrap_assertion_format", "SWT"},
        { "wrap_assertion", signedToken },
    };

WebClient webClient = new WebClient();
byte[] responseBuffer = webClient.UploadValues(acsEndpoint, "POST", postData);
string response = Encoding.UTF8.GetString(responseBuffer);

Now that we've seen how to get a token (in one of two excitingly similar ways) let's take a look at the response.

Understanding the Token

The response is formatted as a Http Form (name1=value1&name2=value2) like this (the sensitive details have been scrubbed to protect the innocent):

 
wrap_access_token=
net.windows.servicebus.action%3dListen%252cManage%252cSend%26http%253a%252f%252fschemas.microsoft.com%252faccesscontrolservice%252f2010%252f07%252fclaims%252fidentityprovider%3dhttps%253a%252f%252fwillpe-blog-sb.accesscontrol.appfabriclabs.com%252f%26Audience%3dhttp%253a%252f%252fwillpe-blog.servicebus.appfabriclabs.com%252f%26ExpiresOn%3d1305157180%26Issuer%3dhttps%253a%252f%252fwillpe-blog-sb.accesscontrol.appfabriclabs.com%252f%26HMACSHA256%3daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%253d
&wrap_access_token_expires_in=
1199

The WRAP Access Token is the piece of this response that we need. It's Url Encoded (i.e. it can be decoded using Uri.UnescapeDataString):

 
net.windows.servicebus.action=Listen,Manage,Send
&https://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider=https://willpe-blog-sb.accesscontrol.appfabriclabs.com/
&Audience=https://willpe-blog.servicebus.appfabriclabs.com/
&ExpiresOn=1305157806
&Issuer=https://willpe-blog-sb.accesscontrol.appfabriclabs.com/
&HMACSHA256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=

The token contains 5 claims and a signature:

  • net.windows.servicebus.action: The permissions this token grants (in this case, Listen, Send and Manage)
  • https://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider: The authority that actually identified the user (ACS, Active Directory, Facebook, etc)
  • Audience: The url that this token applies to
  • ExpiresOn: The time that this token is valid until; expressed as a unix file time (that is the number of seconds past January 1, 1970 at 12:00:00 am (UTC))
  • Issuer: The Security Token Service which issued this security token (ACS)
  • HMACSHA256: The token signature

In your application, you can use these values to figure out when to renew a token, how the user was authenticated and which actions the user is permitted to perform.