Share via


Securely connecting to Azure ServiceBus using WebClient and OAuth WRAP

Introduction

The following illustrates a way to connect to an Azure ServiceBus Queue using a WebClient instead of using the latest APIs supplied in the Azure SDK.  The primary reason for this is to illustrate connectivity from clients that either are not .net based or are limited in their version of .Net.

In this particular situation, we're integrating the Unity 3D game engine which does not support .NET 4.0 as it runs on Mono.  This required us to create a client using  .NET Framework 3.5 and to use a WebClient to post messages to the queue. As well as requiring a change to the client, we also needed to configure my ServiceBus namespace to support ACS.

In August 2014, new namespace's created in the Azure portal stopped supported ACS by default.  As there is no way of adding ACS support, ACS support must be specified when the namespace is created.  More information is supplied in MSDN here.

Sample Project

You will need an Azure subscription and the ability to create a new Service Bus namespace.  Because of this Azure Powershell is also required.

*There are not any requirements for additional packages from NuGet.

A sample project is available for download from the MSDN Gallery.*

Client

The solution contains two projects: a SBClient project and unity test project.  The SBClient illustrates a simple post of a message to an Azure Service Bus Queue using the WebClient class.  The code snippet below shows the actual post is very straightforward:

public static  bool SubmitEvent(string payload)
{
    WebClient webClient = new  WebClient();
    webClient.Headers[HttpRequestHeader.Authorization] = GetToken();
 
    var response = webClient.UploadData(ServiceHttpAddress, "POST", System.Text.Encoding.Default.GetBytes(payload));
    string responseString = Encoding.UTF8.GetString(response);
 
    return string.IsNullOrEmpty(responseString);
}

  

The GetToken method takes a bit more explaining.  MSDN does a better job here.  In our terms, we again use an instance of WebClient to get a token to from the our ACS endpoint in Azure.

private static  string GetToken()
 
{
    var acsEndpoint = "https://" + ServiceNamespace + "-sb." + acsHostName + "/WRAPv0.9/";
 
    // Note that the realm used when requesting a token uses the HTTP scheme, even though
    // calls to the service are always issued over HTTPS
    var realm = "http://" + ServiceNamespace + "." + sbHostName + "/";
 
    NameValueCollection values = new  NameValueCollection();
    values.Add("wrap_name", issuerName);
    values.Add("wrap_password", issuerSecret);
    values.Add("wrap_scope", realm);
 
    WebClient webClient = new  WebClient();
    byte[] response = webClient.UploadValues(acsEndpoint, values);
 
    string responseString = Encoding.UTF8.GetString(response);
 
    var responseProperties = responseString.Split('&');
    var tokenProperty = responseProperties[0].Split('=');
    var token = Uri.UnescapeDataString(tokenProperty[1]);
 
    return "WRAP access_token=\""  + token + "\"";
}

 
In the class there are several strings that will need to be updated with values from the particular Azure ServiceBus namespace (where to locate the values is explained below when setting up Azure):

static string  ServiceNamespace = "<namespace name>"; 
static string  ServiceHttpAddress = "https://<;namespace name>.servicebus.windows.net/<queue name>/messages"; 
  
const string  acsHostName = "accesscontrol.windows.net"; 
const string  sbHostName = "servicebus.windows.net"; 
  
const string  issuerName = "<service identity name>"; 
const string  issuerSecret = "<service identity password>";

Unit Test

The unit test is a bit simple but it does what we need it to do for this sample.

[TestClass] 
public class  ServiceBusWriterTests 
{ 
    [TestMethod] 
    public void  PostNewMessage() 
    { 
        var wasSuccessful = ServiceBusWriter.SubmitEvent("This is a test."); 
  
        Assert.IsTrue(wasSuccessful); 
    } 
}

Setting Up Azure Service Bus

Now for the interesting part.  The first step is to launch Azure PowerShell and add your Azure account using the Add-AzureAccount command:

In this situation, we have two subscriptions so we selected the desired subscription using the Set-AzureSubscription command.

Next is to create a new Azure ServiceBus Namespace with ACS enabled.  In this situation, we specified MyServiceBusQueue but you will need to choose a unique namespace for your namespace.

Notice the AcsManagementEndpoint.  In the Azure portal create a new Queue under the namespace (you can use PowerShell but the portal is simpler).

 

We have enough now to update our source code and get the basic sample running.  See the updated constant strings below:

static string  ServiceNamespace = "MyServiceBusQueue"; 
 
static string  ServiceHttpAddress = "https://myservicebusqueue.servicebus.windows.net/samplequeue/messages"; 
  
const string  acsHostName = "accesscontrol.windows.net"; 
const string  sbHostName = "servicebus.windows.net"; 
  
const string  issuerName = "owner"; 
const string  issuerSecret = "PmAIPe0XGySxvw8zvz/ELfOh2oPTh28Wa3EEzVAkJ3M=";

And when running our test we see that we retrieve the token and submit without a failure:

 

 

ACS Security - Service Identity

Using owner to connect to the service bus is not ideal in most situations where access might be performed by clients outside your control.  In this situation, desktop games will be connecting to a namespace so we wanted to limit the access to only being able to post messages to the queue and wanted to control the password being used.

By creating a service identity in the Azure Access Control Service with send only permission, we can limit access to the queue for those clients.

First, let's create a new service identity called UnityClient.  In the Azure portal, select the namespace and click Connection Information. Be sure to do this at the namespace level and not at the queue level!

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140646/1/connection.png

This then allows us to navigate to the ACS portal:

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140648/1/openacs.png

 

Now we need to create a new Service Identity:

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140649/1/addserviceidentity.png

 

The UnityClient identity and a generated password are shown below:

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140653/1/unityclient.png

 

Now that the service identity is created, we need to amend the rule group for when we connect to the identity has permission to send to the queue.

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140650/1/rulegroup.png

 

The Default Rule Group has three already defined rules for the owner Service identity.  In my terms they are:

  1. If the Service Identity is owner then they can manage
  2. If the Service Identity is owner then they can listen
  3. If the Service Identity is owner then they can send

The following adds a new rule that is basically:

  1. If the Service Identity is UnityClient then they can send

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140651/1/ruleif.png

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140652/1/rulethen.png

 

Back to the Unit Test!

Now that our identity has been set up, we need to update our settings in our client to now use the all-powerful owner but use the limited client:

static string  ServiceNamespace = "MyServiceBusQueue"; 
 
static string  ServiceHttpAddress = "https://myservicebusqueue.servicebus.windows.net/samplequeue/messages"; 
  
const string  acsHostName = "accesscontrol.windows.net"; 
const string  sbHostName = "servicebus.windows.net"; 
  
const string  issuerName = "UnityClient"; 
const string  issuerSecret = "V1nRW+pJV5/62voWmqnDo6dxLC9M/+SfQWUbfuq6iRg=";

And our unit test should still pass!

https://i1.code.msdn.s-msft.com/securely-connecting-to-b1f08adc/image/file/140654/1/unittest2.png

 Summary

The sample project illustrated how to send messages to an Azure Queue using a WebClient and ACS.  In situations where the technology available includes the ability to use the Azure SDK, this is recommended as it simplifies both security and interoperability with Azure.

It is also important to note that access to Azure should be limited to only what is required.  In most situations communication to Azure should be done via specific identity and the use of owner should be restricted.