Click here to monitor SSC
  • Av rating:
  • Total votes: 18
  • Total comments: 5
Anton Staykov

Creating a custom Login page for federated authentication with Windows Azure ACS

29 August 2012

Windows Azure Acess Control Service (ACS) provides a way of authenticating users who need to access web applications and services without having to factor complex authentication logic into the application itself. It is surprisingly easy to create a custom login page that delegates the authentication process to an identity provider

Since we already know how to delegate the login/authentication process to an Identity Provider using Windows Azure ACS (Online Identity Management via Windows Azure ACS and Unified Identity for web apps – the easy way ), let’s see how to create a custom login page and provide users with a seamless experience in our web application.

I will use the WaadacDemo demo code as a base to extend it to host its own login page. The complete code with custom login page is here.

Windows Azure ACS is a really flexible Identity Provider. Besides the default login page, it also provides a number of ways for us to customize the login experience for our users. All the ways are found under Application Integration menu in the Login Pages section:

Once there, we have to choose the Relying Party application for which we would like customization. Now we have a number of options:

ACS Login Page Integration

 Option 1 is the default option we were already using, it’s the ACS hosted login page. What we are interested in is Option 2 – hosting the login page ourselves.

There are two approaches here – the client side and the server side. Both rely to a JSON list of identity providers, the link to the JSON feed URL is displayed in the last text area. If you are the JavaScript/DOM guy – I highly encourage you to download the Example Login Page and play around with the client code provided. In the following lines I’ll describe how to create the Login page with server code.

As already mentioned, both approaches rely on JSON array of identity providers. Since I still don’t like dynamic, I will use a custom type on the server to deserialize my JSON array to. Here is my tiny IdentityProvider class:

public class IdentityProvider
    {
        public List<string> EmailAddressSuffixes { get; set; }
        public string ImageUrl { get; set; }
        public string LoginUrl { get; set; }
        public string LogoutUrl { get; set; }
        public string Name { get; set; }
    }

Now I don’t want to reinvent the wheel, and will use a popular JSON deserializer – NewtonSoft JSON.NET. A simple WebClient call is needed to download the JSON string and pass it to the JSON deserializer:

private List<IdentityProvider> GetIdentityProvidersFromAcs()
    {
        var idPsUrl = ConfigurationManager.AppSettings["IdentityProvidersUrl"];
        var webClient = new WebClient();
        webClient.Encoding = System.Text.Encoding.UTF8;
        var jsonList = webClient.DownloadString(idPsUrl);
        var acsResult = JsonConvert.DeserializeObject<List<IdentityProvider>>(jsonList);
        return acsResult;
     }

You might already have noticed that I use the appSettings section in web.config to keep the URL of the identity providers list. A small gotcha is to explicitly set text encoding of WebClient to UTF-8, because the Live ID identity provider has a small trademark symbol (™) in its name and that cannot be changed. If you do not use UTF-8 encoding, you will have some strange ASCII symbols instead of ( ™ ).

What do we do with the identity providers once we have them? Well, I’ve create a BaseMaster class which all of my MasterPages will inherit.  This BaseMaster class has a property IdentityProviders:

public List<IdentityProvider> IdentityProviders
    {
      get
          {
            List<IdentityProvider> idPs = Cache.Get("idps") 
            as List<IdentityProvider>;
            if (idPs == null)
            {
              idPs = this.GetIdentityProvidersFromAcs();
              Cache.Add("idps", 
                idPs, 
                null,
                DateTime.Now.AddMinutes(20), 
                TimeSpan.Zero, 
System.Web.Caching.CacheItemPriority.Normal, 
                null);
            }
            return idPs;
         }
    }

I also keep the retrieved list in local application cache since this is something not very likely to change often. But, you should leave it for just 20 minutes in the cache to refresh it from time to time.

Now the tricky part. In order to have your own login page we have to combine the force of Forms Authentication along with Windows Identity Foundation and its WSFederatedAuthenticationModule. If you happen to use an ASP.NET MVC application, then it is fairly easy and much discussed. I suggest that you go through Dominic Bayer’s blog post on that subject (and the details). Note that now we will not mix Forms and Claims authentication. We will use claims, but will have the Forms Authentication to help us with that task.

Let’s go through the changes needed in Web.config file. First we set the passiveRedirectEnabled attribute for wsFederation to false:

<wsFederation passiveRedirectEnabled="false"  
           issuer="https://asdemo.accesscontrol.windows.net/v2/wsfederation" 
           realm="http://localhost:2700/"
           requireHttps="false" />

Then we change the Authentication from None for Forms:

<authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" />
    </authentication>

And now add some special Locations to allow all users: Styles, Login.aspx and authenticate.aspx. We need to do that to prevent the Forms Authentication to redirect requests for these URLs. Styles, of course need to be accessed for all users. Login.aspx is our custom login page, and authenticate.aspx is a custom authentication page which we will use as Return URL for our ACS configured relying party application:

Edit Relying Party Application

Here is the code for authenticate.aspx.cs:

protected void Page_Load(object sender, EventArgs e)
     {
       if 
(HttpContext.Current.Request.Form[WSFederationConstants.Parameters.Result] != null)
       {
           // This is a response from the ACS - you can further inspect the message if you want
                SignInResponseMessage message =
         WSFederationMessage.CreateFromNameValueCollection(
         WSFederationMessage.GetBaseUrl(HttpContext.Current.Request.Url),
                    HttpContext.Current.Request.Form)
                    as SignInResponseMessage;

                FormsAuthentication.SetAuthCookie(this.User.Identity.Name, false);
                Response.Redirect("~/Default.aspx");
        }
        else
        {
           Response.Redirect("~/Login.aspx");
        }

     }

We check whether the request contains SignInResponseMessage which would come from ACS. If so, then everything is fine and we must instruct the FormsAuthentication that the user is logged in via the FormsAuthentication.SetAuthCookie(). If not – redirect the user to the Login page.

Finally we will create our custom login page. Just add it as a web form using Master Page. Select the Site.Master (which inherits from BaseMaster). The simplest way of showing Identity Providers I can think of is the Repeater Control:

<h2>Choose Login Method</h2>
    <asp:Repeater runat="server" 
        ID="rptIdentityProviders">
        <ItemTemplate>
            <asp:HyperLink runat="server" 
                NavigateUrl='<%#DataBinder.Eval(Container.DataItem, "LoginUrl")%>' 
                Text='<%#DataBinder.Eval(Container.DataItem, "Name")%>' />
            <br />
            <hr />
        </ItemTemplate>
    </asp:Repeater>

I bind it to the list of identity providers which I got from the BaseMaster:

protected void Page_Load(object sender, EventArgs e)
        {
            this.rptIdentityProviders.DataSource = 
((BaseMaster)this.Master).IdentityProviders;
            this.rptIdentityProviders.DataBind();
        }

Let me visually summarize the process and why it works:

Unauthenticated request flow diagram

If a user requests protected content, the FormsAuthenticationModule kicks and redirects the user to our Login.aspx page. There we show a list of identity providers where one can authenticate. When the user authenticates successfully with the chosen IdP, a token is sent back to the Access Control Service. The ACS processes that token with the configured rules and relying party. If everything passes fine without errors, the ACS sends a new token to the relying party’s Return URL property. In our case this is http://localhost:2700/authenticate.aspx. Since we added this URI as a special location in web.confing, no FormsAuth redirects kicks here. The WsFederationAuthenticationModule intercepts the incoming token and creates the ClaimsIdentity based on that token. It also creates the FederatedAuthentication cookie, which will be used in the subsequent requests to build up the ClaimsIdentity again. Now is the moment to also instruct the FormsAuthenticationModule that the user is already authenticated, so that it doesn’t redirect to Login.aspx.

 I hope you liked it and discovered that it is easier then you thought!

 

 

Anton Staykov

Author profile:

Anton Staykov is Microsoft MVP for the Windows Azure platform, a reward which he has received 3 times already for his continuous devotion to sharing experiences with the communities worldwide. He is a firm believer that in 21st century no single internet user shall ever create new username and password to get access to a service. New business shall respect users' online privacy and let them use their existing online identities. Anton exploits the platform ever since its first public preview back in late 2008. With more than 12 years of developing experience on small and large scales he believes there is no problem that cannot be solved. Tuned on the cloud wave Anton helps smaller and larger enterprises leverage the Windows Azure platform the best way possible, following the best practices and security guidelines outlined by Microsoft. You can follow his cloud blog at http://blogs.staykov.net and in Twitter as @astaykov. Anton is also a regular speaker at local and global conferences (including DevReach, Microsoft Developer Days, Global Windows Azure Boot Camp and others).

Search for other articles by Anton Staykov

Rate this article:   Avg rating: from a total of 18 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject:
Posted by: Azure Mobile Service – Wealth of Information to Get Starte (not signed in)
Posted on: Monday, December 03, 2012 at 8:02 AM
Message: [...] Creating a Custom Login Page for Federated Authentication       with Windows Azure ACS by @astaykov (posted Aug 29) [...]

Subject:
Posted by: Cameron (not signed in)
Posted on: Monday, December 03, 2012 at 8:02 AM
Message: Great read! Thanks for posting Anton.

Subject:
Posted by: Windows Azure and Cloud Computing Posts for 8/31/2012+ - Windows (not signed in)
Posted on: Monday, December 03, 2012 at 8:02 AM
Message: [...] Creating a Custom Login Page for Federated Authentication with Windows Azure ACS by @astaykov (posted Aug 29) [...]

Subject:
Posted by: Windows Azure Community News Roundup (Edition #34) - Windows Azu (not signed in)
Posted on: Monday, December 03, 2012 at 8:02 AM
Message: [...] Creating a Custom Login Page for Federated Authentication with Windows Azure ACS by @astaykov (posted Aug 29) [...]

Subject:
Posted by: Windows Azure Community News Roundup (Edition #34) - Windows Azu (not signed in)
Posted on: Monday, December 03, 2012 at 8:02 AM
Message: [...] Creating a Custom Login Page for Federated Authentication with Windows Azure ACS by @astaykov (posted Aug 29) [...]

 

Top Rated

Data Science Laboratory System – Object-Oriented Databases
 Object-Oriented Databases (OOD) avoid the object-relational impedence mismatch altogether by tightly... Read more...

Tales from a Cloud Software Firm
 Following on from a discussion about how people are using the cloud, the Simple-Talk Editorial Team sat... Read more...

Data Science Laboratory System – Document Store Databases
 A Document Store Database (DSD) is similar to a Relational Database Management system with the... Read more...

Data Science Laboratory System - Instrumentation
 It is sensible to check the performance of different solutions to data analysis in 'lab' conditions.... Read more...

Testing the StreamInsight Service for Windows Azure
 Getting 'up to speed' with StreamInsight is easier if you take the time to run it and test it out.... Read more...

Most Viewed

Windows Azure Virtual Machine: A look at Windows Azure IaaS Offerings (Part 2)
 We continue our introduction of the Azure IaaS by discussing how images and disks are used in the Azure... Read more...

PHPFog and Pagoda Box: A Look at PHP Platforms
 Cloud platforms such as Heroku, AppEngine, PHPFog and Pagoda Box are ideal for companies who just want... Read more...

An Introduction to Windows Azure BLOB Storage
 Azure BLOB storage is persistent Cloud data storage that serves a variety of purposes. Mike Wood shows... Read more...

Managing session state in Windows Azure: What are the options?
 Because you can't maintain session state for ASP.NET applications in Azure using the default in-process... Read more...

Creating a custom Login page for federated authentication with Windows Azure ACS
 Windows Azure Acess Control Service (ACS) provides a way of authenticating users who need to access web... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.