In my previous blog post I demonstrated how to secure a workflow service using the Windows Identity Foundation. With this in place we only allow users that are trusted by the STS Dominick Baier wrote. That is nice but in some cases we might not want all users to be able to perform all actions.

 

How can we check for individual users against a specific action?

Using an STS we don’t get the user credentials from the client application but we do get a lot of information in the form of security tokens from the STS my means of a SAML header. When we are using Windows Identity Foundation the place to check if a user can use a specific resource is using a ClaimsAuthorizationManager. This ClaimsAuthorizationManager is very simple and it contains just a single method we need to override, the CheckAccess(), which returns a boolean if the user is allowed.

The CheckAccess() function is passed an AuthorizationContext which lets us determine the resource requested, the URL of the workflow service in this case, and details about the user. So suppose we only want to let users with the role “Managers” into our service we would need the following code:

public class MyServiceClaimsAuthorizationManager : ClaimsAuthorizationManager
{
    public override bool CheckAccess(AuthorizationContext context)
    {
        var result = true;
 
        var resource = context.Resource.First();
        if (resource.Value == "http://localhost:1533/Service1.xamlx")
        {
            result = (from i in context.Principal.Identities
                      from c in i.Claims
                      where c.ClaimType == ClaimTypes.Role
                      select c).Any(r => r.Value == "Managers");
       }
 
        return result;
    }
}

The Resource property contains whatever we want to access and the Principal property contains the details about the user wanting access. In this case the Thinktecture provides us with the roles the user provides.

We also need to register this in the web.config file using the <microsoft.identityModel><service> section. This can be done by adding the <claimsAuthorizationManager> element as follows:

<microsoft.identityModel>
  <service>
    <audienceUris>
      <add value="http://localhost:1533/Service1.xamlx" />
    </audienceUris>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <trustedIssuers>
        <add thumbprint="7974900A2BB2829BE987C17D2F4503F07C321032" name="http://sample.thinktecture.com/trust/stsdm" />
      </trustedIssuers>
    </issuerNameRegistry>
    <claimsAuthorizationManager type="MyService.MyServiceClaimsAuthorizationManager,MyService"/>
  </service>
</microsoft.identityModel>

With this in place the student can no longer send requests to the workflow service but the manager is free to do so.

 

Authorizing users based on our own data

So far we have been allowing a user in based on the role data for that user. That works fine but suppose we want to use a very different criteria, for example the office location he or she is based in?

The STS is free to add extra claims as it sees fit and we can use any claim we want to authorize users. And the STS Dominick Baier wrote also contains a claim for which office the user is based in. Suppose we only want to allow users based in the Zoetermeer office to use our web service we could change our ClaimsAuthorizationManager to the following:

 

using System.Linq;
using Microsoft.IdentityModel.Claims;
 
namespace MyService
{
    public class MyServiceClaimsAuthorizationManager : ClaimsAuthorizationManager
    {
        public override bool CheckAccess(AuthorizationContext context)
        {
            var result = true;
 
            var resource = context.Resource.First();
            if (resource.Value == "http://localhost:1533/Service1.xamlx")
            {
                result = (from i in context.Principal.Identities
                          from c in i.Claims
                          where c.ClaimType == "http://sample.thinktecture.com/claims/office"
                          select c).Any(r => r.Value == "Zoetermeer");
            }
 
            return result;
        }
    }
}

 

Using this query against the custom http://sample.thinktecture.com/claims/office claim we only allow users from the Zoetermeer office. Nice as our service has nothing to do about managing user locations, all of that is done centrally my the STS [:)]

Enjoy!

 

www.TheProblemSolver.nl

Wiki.WindowsWorkflowFoundation.eu