次の方法で共有


Azure Access Control Service Client Patterns Part 1

One of the very cool features of the Windows Azure AppFabric, which is now commercially available as of the 12th of April 2010, is the Access Control service. This service provides authentication services for not only your Azure based cloud services, but your local services as well.  It provides a foundation for federated identity across the Internet.

Here is the $0.50 explanation of Access Control.

The Windows Azure platform AppFabric Access Control service simplifies access control for Web service providers by lowering the cost and complexity of integrating with various customer identity technologies. Instead of having to address different customer identity technologies, Web services can easily integrate with AppFabric Access Control. Web services can also integrate with all identity models and technologies that AppFabric Access Control supports through a simple provisioning process and through a REST-based management API. Subsequently, Web services can allow AppFabric Access Control to serve as the point of integration for service consumers.

Here are some of the features of the AppFabric Access Control Service (ACS):

  • Usable from any platform.
  • Low friction way to onboard new clients.
  • Integrates with AD FS v2.
  • Implements OAuth Web Resource Authorization Protocol (WRAP)and Simple Web Tokens (SWT).
  • Enables simple delegation.
  • Trusted as an identity provider by the AppFabric Service Bus.
  • Extensible to provide integration with any identity provider.

Say for example that you want to make your internet exposed web application or web service available to internet customers. You want some form of authentication, but you don’t want all of the hassle of re-inventing an authentication system and be bothered storing the credentials of potentially thousands of internet based users. The ACS can help here.

I thought it would be a good idea to explain the basic patterns for each of the components involved in an Access Control scenario. There are always three components involved in these kinds of application scenarios:

  • Service provider: The REST Web service.
  • Service consumer: The client application that accesses the Web service.
  • Token issuer: The AppFabric ACS itself.

There is a common interaction pattern that all applications integrating with the ACS will follow. This is best depicted in the diagram below:

Access Control service Integration Pattern Diagram

Access Control Service Interaction Pattern
Graphic is from the Azure App Fabric SDK .chm help file.

It is important to note that Step 0. Secret exchange, happens once during configuration of the ACS, and does not require an ongoing connection between the service and the internet based ACS. When the secret key for example needs to be updated, you will use a tool like ACM.exe to set up a new key and distribute the key to the Service Consumers.

I don’t want to discuss the SWT Token contents, WRAP protocol, and all of that stuff here. In the post I just want to give you the patterns that you will apply to your services, and clients in order to interact with the ACS.

NOTE! The code examples here are trimmed down to demonstrate the discussed functionality only. They are NOT considered production ready as they do not contain any error handling, validation, or other security measures in them!

You might want to download the Azure App Fabric SDK and accompanying help file to try out some of the examples.

ACS Client Patterns

The client, also known as the Requestor, needs to obtain a token from the ACS which it will then present to your service as evidence of authentication from the ACS. All token requests sent to the ACS are simple HTTP POST requests. At this point in time there are three methods by which a client can request a token.

  • Plain Text: The service consumer sends a secret key, called an issuer key, directly to ACS for authentication. This requires no encryption operations and is very similar to the username/password model.
  • Signed: The service consumer creates a signed Simple Web Token (SWT) which is then presented to ACS for verification. These SWTs can be signed with the same secret key that is used for Plain Text verification.  ACS will simply use it’s copy of the secret key to verify the signature. This allows you to use a simple secret key, without actually transmitting the key itself.
  • SAML: This is targeted at any service consumer that can obtain a signed SAML Bearer token. This is the primary method by which ADFSV2 will integrate with ACS for federated authentication. This allows the service consumer to use an enterprise identity to authenticate with the ACS.

In order to request one of these tokens the client must be coded to follow one of these patterns:

Plain Text Request Pattern

In this pattern we are using a simple shared secret key as a password to request an SWT from ACS for the service we are trying to gain access to.  This shared secret key would have been loaded into the appropriate ACS endpoint previously by the service owner then sent to us to use as the password to obtain tokens from ACS.

  1. Create a Web Client object to send the request
  2. Create the POST parameters in a NameValueCollection
    1. create the wrap_name which is the ‘username’ for the request.
    2. create the wrap_password which is the secret key.
    3. create the wrap_scope which defines which service we are requesting a token for.
  3. Send the POST request to the ACS
  4. Receive the SWT as a byte array from the ACS
  5. Convert the byte array to a string representation
  6. Add the SWT to the Authentication Header of the request send to the Web Service.

Code Example of the Plain Text Request Pattern

// Step 1 Create web client object.

WebClient client = new WebClient();

client.BaseAddress =

string.Format(https://mysnservice.accesscontrol.windows.net);

// Step 2 Create POST parameters

NameValueCollection values = new NameValueCollection();

// Step 2.1 wrap_name

values.Add("wrap_name", "mysncustomer1");

// Step 2.2 wrap_password

values.Add("wrap_password", "5znwNTZDYC39dqhFOTDtnaikd1hiuRa4XaAj3Y9kJhQ=");

// Step 2.3 wrap_scope

values.Add("wrap_scope", "https://mysnservice.com/services");

// WebClient takes care of the URL Encoding

// Step 3 & 4

byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);

// the raw response from AC

// Step 5

string token = Encoding.UTF8.GetString(responseBytes);

// Reset Client target

client.BaseAddress = "https://mysnservice.com/services";

// Step 6 set Authentication header

string headerValue = string.Format("WRAP access_token=\"{0}\"", HttpUtility.UrlDecode(token));

client.Headers.Add(

"Authorization", headerValue);

NameValueCollection values = new NameValueCollection();

values =

new NameValueCollection();

values.Add(

"Param1", valueOfParam1);

byte[] serviceResponseBytes = client.UploadValues(string.Empty, values);

return Encoding.UTF8.GetString(serviceResponseBytes);

Signed Token Request Pattern

In this pattern we take the secret key we received from the service owner and use it to sign an SWT that we create and send to ACS for verification. ACS will then send us a new SWT that is appropriate for the service we are requesting access to. As with the previous pattern the request to ACS for the token is sent over an HTTP POST request.

To create our own signed tokens, we have to be able to assemble an SWT. The full protocol and build of SWTs can be found at this link.  Essentially, an SWT contains the following:

  • Issuer– Looks up the key used to sign the token. If the signature is valid, then this value is used to perform output claim calculation.
  • Audience – If present, AC uses this value to ensure that AC is the intended target of the SWT.
  • ExpiresOn – If present, indicates whether the token is expired.
  • Additional Claims – If present, AC uses these parameters to perform output claim calculation. Each claim type must appear only once. Multiple claim values of the same claim type must be concatenated together with a "," (comma) character.
  • HMACSHA256 – Validates the SWT signature and looks up the issuer key named in the Issuer parameter.

If you are going to take this route, I would highly recommend creating a TokenFactory that you can use to issues your self-signed tokens. The TokenFactory would look like this:
(The code is pretty self-explanatory)

using

System;

using

System.Collections.Generic;

using

System.Linq;

using

System.Security.Cryptography;

using

System.Text;

using

System.Web;

public

class TokenFactory

{

string signingKey;

string issuer;

public TokenFactory(string issuer, string signingKey)

{

this.issuer = issuer;

this.signingKey = signingKey;

}

public string CreateToken()

{

StringBuilder builder = new StringBuilder();

// add the issuer name

builder.Append(

"Issuer=");

builder.Append(

HttpUtility.UrlEncode(this.issuer));

string signature = this.GenerateSignature(builder.ToString(), this.signingKey);

builder.Append(

"&HMACSHA256=");

builder.Append(signature);

return builder.ToString();

}

private string GenerateSignature(string unsignedToken, string signingKey)

{

HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(signingKey));

byte[] locallyGeneratedSignatureInBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(unsignedToken));

string locallyGeneratedSignature = HttpUtility.UrlEncode(Convert.ToBase64String(locallyGeneratedSignatureInBytes));

return locallyGeneratedSignature;

}

}

A signed token request only contains two parameters:

  • a URL encoded wrap_scope parameter.
  • The URL-encoded wrap_assertion parameter which is a signed SWT that we create.

There is also one additional step in this process. If the request is successfully authenticated, the ACS returns two form-encoded parameters: wrap_token and wrap_token_expires_in. The values of these parameters are the actual token and the approximate remaining lifetime of the token (in seconds), respectively.

Before sending the token to the Web service, the service consumer must extract and URL-decode the token from the response. If the Web service requires the token to be presented in the HTTP Authorization header, then the token must be preceded by the scheme WRAPv0.9.

To do this, I recommend you create an SwtUnpacker that you can reuse to return a pre-built auth header value.  It would look something like this: (again the code is self explanatory)

using

System;

using

System.Collections.Generic;

using

System.Linq;

using

System.Text;

namespace

Microsoft.AccessControl.SDK.GettingStarted.Client

{

class SwtUnpacker

{

/// <summary>

/// This will unpack the token received from a successful ACS Token request when submitting

/// an SWT as authentication.

/// </summary>

/// <param name="acsResponse"></param>

/// <returns>The authorization header to be sent to the target web service.</returns>

public static string UnpackToken(string acsResponse)

{

string token = acsResponse

.Split(

'&')

.Single(value => value.StartsWith(

"wrap_token=", StringComparison.OrdinalIgnoreCase))

.Split(

'=')[1];

return "WRAPv0.9" + " " + HttpUtility.UrlDecode(token);

}

}

}

  1. Create our own SWT which we will sign and send to the ACS
    1. Call the TokenFactory above with the appropriate issuer (username) and secret key.
  2. Create a Web Client object to send the request
  3. Create the POST parameters in a NameValueCollection
    1. Create the wrap_scope parameter which defines which service we are requesting a token for.
    2. Create the wrap_assertion_format parameter which tells the ACS how to read out request.
    3. Create the wrap_assertion which is our URL-encoded signed token that we are presenting for authentication to the ACS
  4. Send the POST request to the ACS
  5. Receive the SWT as a byte array from the ACS
  6. Convert the byte array to a string representation
  7. Unwrap the SWT from ACS
  8. Add the SWT to the Authentication Header of the request send to the Web Service.

// Step 1: Create our own Signed SWT

TokenFactory tf = new TokenFactory("mysncustomer1". "5znwNTZDYC39dqhFOTDtnaikd1hiuRa4XaAj3Y9kJhQ=");

string myToken = tf.CreateToken();

// Step 2: create a web client object to send the request

WebClient client = new WebClient();

client.BaseAddress =

string.Format("https://mysnservice.accesscontrol.windows.net");

// Step 3: Create the POST parameters

NameValueCollection values = new NameValueCollection();

// Step 3.1 add the wrap_scope

values.Add(

"wrap_scope", "https://mysnservice.com/services");

// Step 3.2 add the format

values.Add(

"wrap_assertion_format", "SWT");

// Step 3.3 add our URL Encoded signed token

values.Add(

"wrap_assertion", myToken);

// WebClient takes care of the remaining URL Encoding

// Step 4 & 5 send the POST request to the

byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);

// Step 6 convert the response to a string

string response = Encoding.UTF8.GetString(responseBytes);

// Step 7 unpack token

string authToken = SwtUnpacker.UnpackToken(response);

// Step 8 add the header to the call and send to the web service.

client.Headers.Add(

"Authorization", authToken);

// From this point it is just like the previous pattern to call the target web service.

// Add the parameters, send the request and interpret the response.

 

This should start you on your way to using self-contained methods to call the Azure access Control service to retrieve an authentication token to present to a target web service. 

In Part 2, we will look at the patterns for integration with ADFSV2 SAML tokens and the Web Service token acceptance pattern.

Comments

  • Anonymous
    July 01, 2011
    this is an absolutely terrible article.  i may know a little more now but that doesn't help me get started. wow

  • Anonymous
    January 20, 2013
    Sorry you couldn't get what you wanted but without specific points to address no one can help you. As the title states this is which level look at the patterns, not a detailed how to, or instructional post