How to: Implement Claims Authorization in a Claims-Aware ASP.NET Application Using WIF and ACS

Updated: June 19, 2015

Applies To: Azure

Applies To

  • Microsoft® Azure™ Access Control Service (ACS)

  • Windows® Identity Foundation (WIF)

  • ASP.NET

Summary

This topic describes how to implement Claims Based Access Control (CBAC) authorization in claims-aware ASP.NET web applications using WIF and ACS.

Contents

  • Objectives

  • Overview

  • Summary of Steps

  • Step 1 – Implement a Claims Authorization Library

  • Step 2 – Configure an ASP.NET Application to Use the Claims Authorization Library

  • Step 3 – Configure an Authorization Policy

  • Related Items

Objectives

  • Externalize the authorization logic from the application.

  • Implement the authorization checks beyond roles, resulting in a more detailed control over authorization.

  • Provide for policy driven, declarative, authorization management.

Overview

Claims authorization is based on using the information that is carried within a token—the claims. Claims authorization is useful, when it is insufficient to make the authorization decision based solely on roles. For example, in financial applications, the same role can be given to different limits for transferring money between accounts based on tenure and other attributes. Such attributes may come in the form of a claim along with the role and therefore allow for a more finely tuned authorization decision.

Summary of Steps

  • Step 1 – Implement a Claims Authorization Library

  • Step 2 – Configure an ASP.NET Application to Use the Claims Authorization Library

  • Step 3 – Configure an Authorization Policy

Step 1 – Implement a Claims Authorization Library

This step shows you how to create an external class library that will read an access policy from a configuration file and enforce the access checks. The following sample shows how to implement a claims authorization library that makes an authorization decision based on a ZIP code claim.

To implement claims authorization library

  1. Make sure you are running Visual Studio® in Administrator mode.

  2. Right-click the desired solution in Solution Explorer, add a new Class Library project, and then give it a name, for example, MyClaimsAuthorizationManager.

  3. Delete the default class, Class1.cs.

  4. Add a new class and give it a name, for example, ZipClaimsAuthorizationManager. This will implement an authorization based on postal codes.

  5. Add a reference to the Microsoft.IdentityModel and to the System.Web assemblies.

  6. Add the following declarations.

    using System.IO;
    using System.Xml; 
    using Microsoft.IdentityModel.Claims; 
    using Microsoft.IdentityModel.Configuration; 
    
  7. Extend the ClaimsAuthorizationManager class, overriding its AccessCheck method, and then implementing a constructor to your ZipClaimsAuthorizationManager class. Your code should look similar to the following.

    namespace MyClaimsAuthorizationManager 
    { 
        class ZipClaimsAuthorizationManager : ClaimsAuthorizationManager 
        { 
            private static Dictionary<string, int> m_policies = new Dictionary<string, int>(); 
    
            public ZipClaimsAuthorizationManager(object config) 
            { 
                XmlNodeList nodes = config as XmlNodeList; 
                foreach (XmlNode node in nodes) 
                { 
                    { 
                        //FIND ZIP CLAIM IN THE POLICY IN WEB.CONFIG AND GET ITS VALUE 
                        //ADD THE VALUE TO MODULE SCOPE m_policies 
                        XmlTextReader reader = new XmlTextReader(new StringReader(node.OuterXml)); 
                        reader.MoveToContent(); 
                        string resource = reader.GetAttribute("resource"); 
                        reader.Read(); 
                        string claimType = reader.GetAttribute("claimType"); 
                        if (claimType.CompareTo(ClaimTypes.PostalCode) == 0) 
                        { 
                            throw new ArgumentNullException("Zip Authorization is not specified in policy in web.config"); 
                        } 
                        int zip = -1; 
                        bool success = int.TryParse(reader.GetAttribute("Zip"),out zip); 
                        if (!success) 
                        { 
                            throw new ArgumentException("Specified Zip code is invalid - check your web.config"); 
                        } 
                        m_policies[resource] = zip; 
                    } 
                } 
            } 
            public override bool CheckAccess(AuthorizationContext context) 
            { 
                //GET THE IDENTITY 
                //FIND THE POSTALCODE CLAIM'S VALUE IN IT 
                //COMPARE WITH THE POLICY 
                int allowedZip = -1; 
                int requestedZip = -1; 
                Uri webPage = new Uri(context.Resource.First().Value); 
                IClaimsPrincipal principal = (IClaimsPrincipal)HttpContext.Current.User; 
                if (principal == null) 
                { 
                    throw new InvalidOperationException("Principal is not populate in the context - check configuration"); 
                } 
                IClaimsIdentity identity = (IClaimsIdentity)principal.Identity; 
                if (m_policies.ContainsKey(webPage.PathAndQuery)) 
                { 
                    allowedZip = m_policies[webPage.PathAndQuery]; 
                    requestedZip = -1; 
                    int.TryParse((from c in identity.Claims 
                                            where c.ClaimType == ClaimTypes.PostalCode 
                                            select c.Value).FirstOrDefault(), out requestedZip); 
                } 
                if (requestedZip!=allowedZip) 
                { 
                    return false; 
                } 
                return true; 
            } 
        } 
    }
    
  8. Compile the solution to make sure that there are no compilation errors.

  9. Locate the compiled library, in this case MyClaimsAuthorizationManager.dll. It will need to be dropped into the bin folder of the ASP.NET web application.

Step 2 – Configure an ASP.NET Application to Use the Claims Authorization Library

The next steps are performed in your ASP.NET web application. Do not add a reference to your Claims Authorization Manager library that you created in previous step. Your ASP.NET web application should be “unaware” of it, except in web.config.

To configure an ASAP.NET application to use the Claims Authorization Library

  1. Configure your ASP.NET web application to include WIFs ClaimsAuthorizationModule in its pipeline by adding the following entry to the HttpModules section in the web.config (valid for the development web server that ships with Visual Studio; for Internet Information Services (IIS) version 7, this configuration should be located in the modules section under the system.webServer node).

    <add name="ClaimsAuthorizationModule" 
         type="Microsoft.IdentityModel.Web.ClaimsAuthorizationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> 
    
  2. Configure your ASP.NET web application to use the custom authorization library you implemented in the previous step by adding the following entry to the microsoft.identityModel.service node.

    <claimsAuthorizationManager type="MyClaimsAuthorizationManager.ZipClaimsAuthorizationManager, MyClaimsAuthorizationManager" >
    

Step 3 – Configure an Authorization Policy

In this topic the configuration policy is expressed in the ASP.NET web application web.config file. The policy carries simple rules that map resources to the claim type and its value. For example, the following policy can be read and enforced by the custom authorization library you created and configured in the previous steps. Add the following entry to the claimsAuthorizationManager node you configured in the previous step.

<policy resource="/default.aspx">
  <claim claimType=https://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode
  Zip="11111" />
</policy>

The above policy requires that an incoming token must have a zip claim with a value of 11111 in order to access the default.aspx page.