Web services, as we’ve learned throughout this series, are integral to the workings of Windows Azure Pack. Every UI exposed to the user connects to the backend via web service, every resource provider is managed by Windows Azure Pack through their own web services, and 3rd party functionality can be tied in through web services. It’s an SOA world.

Last time we looked at the Tenant Public API and how it uses client certificates for authentication. Client certificates are paradoxically complex beasts while also being the easiest authentication method for 3rd parties to use. This is because you don’t really need to do anything more once you’ve gotten the client key – you just attach it to the request (or rather the TLS tunnel) and go. The hard parts are generating the key and securely storing the key. On the other hand, certificates can also make authorization a bit trickier because you can’t easily store extra information about the user which means its up to the service to determine who can do what through some custom ACL.

This of course is where claims and federation comes in handy – delegate the role of determining permissions to another service and simply trust what it says the user can and cannot do. Easy, right? Well, Maybe.

In the case of the Tenant Public API we have to deal with two things: path of least resistance to getting a user authenticated, and compatibility with Microsoft Azure’s public cloud service API. If you require an authorization token from a 3rd party you’ll make authentication waaaaaaay harder (there goes that least resistance thing), and will break compatibility with the public cloud API. This is probably why it took so long for Microsoft to move beyond client certificates in the public cloud.

On the other hand, if you could use something other than client certificates, say a JWT, you have way more flexibility when working with the services. If you use a token:

  • You have more control over authentication: you don’t necessarily need to authenticate with client certificates
  • You can have better control over authorization: you can dictate token lifetime at the STS instead of requiring you to delete the client certificate from the management portal
  • You can potentially call other web services with the same token without requiring those services to explicitly trust your client certificate

From a futures perspective, if we could use a token instead of a client certificate we could include permissions (rights, scopes, etc.) that dictates what the holder of the token is allowed to do, such as create VMs, delete web sites, manage databases, etc. At present Windows Azure Pack doesn’t support fine-grained authorization within a subscription — but imagine if it could. This would let the STS decide who can do what instead of tying permissions to a credential in the portal.

Tenant Trust Relationship

Tenant Trust Relationship

But getting back to the here and now if we could do away with client certificates we are given a bit more freedom with how we work with the Tenant Public API. As it turns out, we can.

It’s possible to interact with the Tenant Public API with a JWT in a request Authorization header without requiring a client certificate. Cool! However, this functionality isn’t enabled by default.

Sufficiently Scary Warning
To enable authentication by token you need to update the API web.config, and more importantly, need to understand the implications of not using a client certificate. First, client certificates provide mutual authentication – both the client and the server have some assurance that no one is eavesdropping on the conversation – removing client certificates changes that assurance. Second, you need to trust the token issuer is secure.

Both of these change the entire trust model of the Public API and you need to work out a proper threat model that fits your new requirements.

Configuring the Tenant Public API to authenticate by token requires switching the TenantServiceMode property of the site to a hybrid mode. These modes dictate how authentication is expected to occur within the site. The default mode is DefaultTenant which means to authenticate by client certificate. We want to switch it to HybridTenant. This gives us the option of either authenticating by client certificate or authenticating by token – best of both worlds!

To switch the mode you will need to update the Tenant Public API web.config, which will require unprotecting it.

Unprotect-MgmtSvcConfiguration -Namespace TenantPublicAPI

Next, you’ll need to locate the appSetting TenantServiceMode and set it toHybridTenant.

<add key="TenantServiceMode" value="HybridTenant" />

That’s all you need to enable token authentication.

This of course still leaves us with a few questions: who issues the tokens, and how do we include the tokens in the service request?

The first question is easy to answer: the issuer is the same issuer who issues tokens for the Tenant API. Meaning… if you’re using ADFS to log into the Tenant Portal then the issuer is ADFS. If you’re using the built in authentication site, then the issuer is the built in authentication site. Getting the token is dependent on the STS, and how you plan on using it. If your STS supports WSTrust you can get a token issued to you. If you’re using the built in Tenant Authentication Site you can call into the WSTrust endpoint: https://youauthsite.yourdomain.com/wstrust/issue/usernamemixed.

To be issued a JWT you need to specify the proper TokenType in the Request for SecurityToken (RST). The RST Response (RSTR) contains a generic XML-wrapped base64 encoded representation of your JWT.

private static string RequestJwtToken()
{
      const string StsEndpoint = "https://sts.syfuhs.net/wstrust/issue/usernamemixed";

      var binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
      binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
      binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
      binding.Security.Message.EstablishSecurityContext = false;

      var factory = new WSTrustChannelFactory(binding, new EndpointAddress(StsEndpoint));
      factory.TrustVersion = TrustVersion.WSTrust13;
      factory.Credentials.UserName.UserName = "steve@syfuhs.net";
      factory.Credentials.UserName.Password = "supersecretshiny";
      var channel = factory.CreateChannel();

      RequestSecurityTokenResponse rstr = null;
      var resp = channel.Issue(new RequestSecurityToken()
      {
            RequestType = RequestTypes.Issue,
            TokenType = "urn:ietf:params:oauth:token-type:jwt",
            AppliesTo = new EndpointReference("https://azureservices/TenantSite")
      }, out rstr);

      GenericXmlSecurityToken xmlToken = resp as GenericXmlSecurityToken;

      if (xmlToken == null)
            return null;

      return Encoding.Unicode.GetString(Convert.FromBase64String(xmlToken.TokenXml.InnerXml));
}

You’ll notice the authentication bit with the factory credentials? If you’re using your own STS then this is something you’d have to work out with the STS, otherwise in this case the credentials are your Tenant Portal credentials.

Now that you’ve got a JWT you just need to stick it into your request header. This isn’t all that different from how we called into the API with a client certificate last time:

private static void HttpWebRequestJwtTest(Uri requestUri, string jwtToken)
{
      HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("https://api-cloud.syfuhs.net/subscriptions/386dd878-64b8-41a7-a02a-644f646e2df8"));
      request.Headers.Add("x-ms-version", "2010-10-28");
      request.Method = "GET";
      request.ContentType = "application/json";
      request.Headers.Add("Authorization", "Bearer " + jwtToken);

      try
      {
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            ProcessResponse(response);
      }
      catch (WebException e)
      {
            Console.WriteLine(e.Message);
            Console.WriteLine("------------------------------------");
            ProcessResponse(e.Response as HttpWebResponse);
      }
}

You’ll notice we don’t include the client certificate, but do add the new request header. Our request header would look something like this:

GET https://api-cloud.syfuhs.net/subscriptions/386dd878-64b8-41a7-a02a-644f646e2df8 HTTP/1.1
 x-ms-version: 2010-10-28
 Content-Type: application/json
 Authorization: Bearer eyJ0eXAiOiJKV…snip…c_wjW5YnRg
 Host: api-cloud.syfuhs.net

And the output of our tool:

Response status code: OK
 Pragma: no-cache
 Content-Length: 1074
 Cache-Control: no-cache
 Content-Type: application/json; charset=utf-8
 Date: Sat, 17 May 2014 22:09:39 GMT
 Expires: -1
 Server: Microsoft-IIS/8.5
 X-AspNet-Version: 4.0.30319
 X-Powered-By: ASP.NET
 SubId: 386dd878-64b8-41a7-a02a-644f646e2df8

At this point we should now be able to call into the Tenant Public API with a JWT instead of a client certificate.

Also, if you want to play around with this code take a look at my GitHub repository too. It shows off how you can also do this with the much beloved Web API HttpClient.