Ok …This is in continuation of my previous blog here. Now we are going to integrate ACS with our cloud hosted WCF data service. Following are the steps taken :

  • Create a ACS Namespace
  • Add our Cloud hosted WCF service as Relying Party in ACS namespace
  • Add service identity with the user credentials to be passed from the client in ACS namespace
  • Add claim rules in a rule group associated with your relying party
  • Create an HTTP Module in Cloud Service Web role to intercept when ever a request comes in, it will check whether there is a token in the authorization  header and validate the same. If a valid token is found, access is granted otherwise the request is marked unauthorized
  • Fire request from client(Windows Store Application) using the service identity user credentials configured for the ACS

So lets get started..

-Create a new ACS Namespace from Azure Portal

image

-Click on Manage on the ACS Namespace created to open the ACS Portal from Azure portal

image

-Configure your Cloud Service WCF Data Service url in the realm and return url on the Relying Party Applications section

We will be using Single Web Token(SWT)

image

-Add Service Identity to authenticate directly with ACS and receive security tokens. These credentials will be included in the client request to authenticate itself

image

-Add Claim Rules in a rule group associated with your relying party or generate pass-through rules using the rule group editor

image

-Finally Add an HTTP Module in your Cloud service project to intercept every request and check for the Authorization header to

check if a valid token is sent from the client. Republish the service to Azure with the security integrated.

public class SWTModule : IHttpModule
   {
       //Use a configuration file to manage this data
       string acsHostName = "accesscontrol.windows.net";
       string serviceNamespace = "wcfacsnspace";
       string trustedTokenPolicyKey = "[YOUR TOKEN SIGNING KEY";
       string trustedAudience = "[YOUR CLOUD SERVICE RELYING PARTY URL]";

       void IHttpModule.Dispose() {}

       void IHttpModule.Init(HttpApplication context)
       {
           context.BeginRequest += new EventHandler(context_BeginRequest);
       }

       void context_BeginRequest(object sender, EventArgs e)
       {          
           string headerValue = string.Empty;
           try
           {
               //if (!HttpContext.Current.Request.IsLocal)
               //{
               //Handle SWT token validation
               // Get the authorization header
                headerValue = HttpContext.Current.Request.Headers.Get("Authorization");

               // Check that a value is there
                if (string.IsNullOrEmpty(headerValue))
                {
                   throw new FaultException("Unauthorized");

               }     
           }
         
       }
   }

Web.config

<modules>

<add name="SWTModule" type="WCFServiceWebRole.SWTModule, WCFServiceWebRole" />

</modules>

Now if you try to access the service url from the browser, an unauthorized access error is received

Accessing the service from Windows Store App Client

-Create a Windows Store Application and get token from ACS passing in the realm and set the authorization header.

string token = await TokenReceiver.GetTokenFromACS(realm);
            header = string.Format("WRAP access_token=\"{0}\"", token);

           MySampleDBEntities context = new MySampleDBEntities(new Uri(realm));
           context.SendingRequest += context_SendingRequest;

           DataServiceQuery<PersonInfo> query = (DataServiceQuery<PersonInfo>)(
               from item in context.PersonInfoes
               select item);

static void context_SendingRequest(object sender, SendingRequestEventArgs e)
      {
          e.RequestHeaders["Authorization"] = header;
      }

public class TokenReceiver
   {
       static string serviceNamespace = "[YOUR ACS NAME SPACE]";
       static string acsHostUrl = "accesscontrol.windows.net";
       static string wrapUsername = "[USER ID IN SERVICE IDENTITY]";
        static string wrapPassword = "[PWD IN SERVICE IDENTITY]";

       public static async Task<string> GetTokenFromACS(string scope)
       {
           // request a token from ACS
           HttpClient client = new HttpClient();
           client.BaseAddress =new Uri(string.Format("https://{0}.{1}", serviceNamespace, acsHostUrl));
           HttpContent content = new FormUrlEncodedContent(new[]
           {
                new KeyValuePair<string, string>("wrap_name", wrapUsername),
                new KeyValuePair<string, string>("wrap_password", wrapPassword),
                 new KeyValuePair<string, string>("wrap_scope", scope)
            });
           content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

           var result = await client.PostAsync("WRAPv0.9/",content);
            byte[] responseBytes = await result.Content.ReadAsByteArrayAsync();
           string response = UTF8Encoding.UTF8.GetString(responseBytes, 0, responseBytes.Length - 1);

           return System.Net.WebUtility.UrlDecode(
               response
                .Split('&')
               .Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase))
               .Split('=')[1]);
       }
   }

When the client provides token received from ACS to the Cloud Service using the Service identity credentials, the service functionality is accessible to the client

image

Learn and enjoy Smile !!!!!

A quick disclaimer: The posts/opinions over here are my own views and not of my employer.