Custom Principals and WCF

The question how to setup a custom principal in WCF services comes up every once in a while. Since it is not obvious how this works, I knocked up a little walkthrough and a boilerplate sample.

Principal Permission Mode
WCF has two authorization systems – roles-based and claims-based. Roles-based security is centered around an IPrincipal implementation placed on Thread.CurrentPrincipal. What WCF puts on T.CP depends on the principalPermissionMode attribute in the serviceAuthorization behavior. There are four options:

  • None. T.CP will contain a GenericPrincipal with no roles set.
  • UseWindowsGroups. T.CP will contain a WindowsPrincipal. IsInRole queries the Windows token for group membership. Only supported for Windows authentication.
  • UseAspNetRoles. T.CP will contain a RoleProviderPrincipal. The IsInRole method calls the role provider’s IsUserInRole implementation.
  • Custom. You have to set your own principal implementation.

Authorization Policies
The place to set a custom principal implementation is a class implementing the IAuthorizationPolicy interface. Generally, authorization policies are used to add claim sets to the authorization context, but in this scenario the policy has the special purpose of setting the principal. You will also get a runtime exception when the principalPermissionMode is set to custom and you don’t set a principal.

The IAuthorizationPolicy interface has three member that you have to implement:

  • Id. You typically return a unique ID here, e.g. a GUID.
  • Issuer. This returns a claim set describing who has added the policy information.
  • Evaluate. You create and set the custom principal here.

The way to get to the authenticated identity and the principal in the Evaluate method is a little “hidden”. The evaluation context that gets passed into Evaluate contains a collection named Properties. This collection has two well known keys called Identities and Principal (see also here).

To get to the client identity you have to extract an IList<IIdentity> from this collection:

private IIdentity GetClientIdentity(EvaluationContext evaluationContext)
{
    object obj;
    if (!evaluationContext.Properties.TryGetValue("Identities", out obj))
        throw new Exception("No Identity found");

    IList<IIdentity> identities = obj as IList<IIdentity>;
    if (identities == null || identities.Count <= 0)
        throw new Exception("No Identity found");

    return identities[0];
}

Based on that identity you typically create your custom principal and set it back to the properties collection. The WCF plumbing takes the principal from there and sets it on T.CP.

The following authorization policy combines the groups of a Windows user with application defined roles and creates a new principal that can be queried in the service implementation using Thread.CurrentPrincipal.IsInRole() or PrincipalPermission[Attribute]. The custom principal that is used here is omitted but can be found in the download (it simply mimics the GenericPrincipal, but allows to get back the roles using the Roles property – which can be handy and is a limitation of GenericPrincipal IMO).

class CombinedRolesPolicy : IAuthorizationPolicy
{
    Guid id = Guid.NewGuid();

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        // will hold the combined roles
        List<string> roles = new List<string>();

        // get the authenticated client identity
        IIdentity client = GetClientIdentity(evaluationContext);

        // this policy is intended for Windows accounts only
        WindowsIdentity windowsClient = client as WindowsIdentity;
        if (windowsClient == null)
        {
            throw new SecurityTokenValidationException(
"Only Windows accounts supported"); } // add Windows groups roles.AddRange(GetWindowsRoles(windowsClient)); // add application defined roles roles.AddRange(GetAppRoles(windowsClient)); // set a new principal holding the combined roles // this could be your own IPrincipal implementation evaluationContext.Properties["Principal"] = new CustomPrincipal(windowsClient, roles.ToArray()); return true; } private IEnumerable<string> GetWindowsRoles(WindowsIdentity windowsClient) { List<string> roles = new List<string>(); IdentityReferenceCollection groups =
windowsClient.Groups.Translate(typeof(NTAccount)); foreach (IdentityReference group in groups) { roles.Add(group.Value); } return roles; } private IEnumerable<string> GetAppRoles(WindowsIdentity windowsClient) { List<string> roles = new List<string>(); // simulated database access roles.Add("MyApplicationRoleA"); roles.Add("MyApplicationRoleB"); return roles; } public System.IdentityModel.Claims.ClaimSet Issuer { get { return ClaimSet.System; } } public string Id { get { return id.ToString(); } } }

The last step is to register the authorization policy with your service:

<behaviors>
  <serviceBehaviors>
      <serviceAuthorization principalPermissionMode="Custom">
        <authorizationPolicies>
          <add policyType="LeastPrivilege.CombinedRolesPolicy, CustomPrincipalService" />
        </authorizationPolicies>
      </serviceAuthorization>
    </behavior>
  </serviceBehaviors>
</behaviors>

In you service implementation, you can use the usual techniques to make role based checks, or you cast the principal to its concrete type to use some specialized features you have implemented (see the sample code). Have fun.

WcfCustomPrincipal.zip (24.83 KB)

 

This entry was posted in Uncategorized. Bookmark the permalink.

6 Responses to Custom Principals and WCF

  1. blop says:

    link WcfCustomPrincipal.zip broken

  2. Chris says:

    Dominick, you mention that Role-based is centered on an IPrincipal implementation, but could we say the same thing about claims-based, except that it is based on a ClaimsPrincipal (not really anything to do IPrincipal, I know)? You mention it is based on an implementation placed on T.CP, and I am assuming the same thing applies to a ClaimsPrincipal. Am I correct, or is ClaimsPrincipal expected to be used and set differently? Just looking for some guidance as I am using ClaimsPrincipal but I was going to set it the same way as I use the IPrincipal based stuff.

  3. granadacoder says:

    Thanks! A small tweak to use ClaimsPrincipal instead of CustomPrincipal.

    // add application defined roles
    roles.AddRange(GetAppRoles(windowsClient)); /* this line already exists */

    ICollection claims = new List();
    roles.ForEach(s => claims.Add(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, s)));

    ClaimsIdentity ci = new ClaimsIdentity(windowsClient, claims);
    evaluationContext.Properties[“Principal”] = new ClaimsPrincipal(ci);

    return true; /* this line already exists */

Leave a comment