How to: Create a Custom Authorization Manager for a Service

The Identity Model infrastructure in Windows Communication Foundation (WCF) supports an extensible claims-based authorization model. Claims are extracted from tokens and optionally processed by custom authorization policies and then placed into an AuthorizationContext. An authorization manager examines the claims in the AuthorizationContext to make authorization decisions.

By default, authorization decisions are made by the ServiceAuthorizationManager class; however these decisions can be overridden by creating a custom authorization manager. To create a custom authorization manager, create a class that derives from ServiceAuthorizationManager and implement CheckAccessCore method. Authorization decisions are made in the CheckAccessCore method, which returns true when access is granted and false when access is denied.

If the authorization decision depends on the contents of the message body, use the CheckAccess method.

Because of performance issues, if possible you should redesign your application so that the authorization decision does not require access to the message body.

Registration of the custom authorization manager for a service can be done in code or configuration.

To create a custom authorization manager

  1. Derive a class from the ServiceAuthorizationManager class.

    public class MyServiceAuthorizationManager : ServiceAuthorizationManager
    {
    
    
    Public Class MyServiceAuthorizationManager
        Inherits ServiceAuthorizationManager
    
    
  2. Override the CheckAccessCore(OperationContext) method.

    Use the OperationContext that is passed to the CheckAccessCore(OperationContext) method to make authorization decisions.

    The following code example uses the FindClaims(String, String) method to find the custom claim http://www.contoso.com/claims/allowedoperation to make an authorization decision.

    protected override bool CheckAccessCore(OperationContext operationContext)
    {
      // Extract the action URI from the OperationContext. Match this against the claims
      // in the AuthorizationContext.
      string action = operationContext.RequestContext.RequestMessage.Headers.Action;
    
      // Iterate through the various claim sets in the AuthorizationContext.
      foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
      {
        // Examine only those claim sets issued by System.
        if (cs.Issuer == ClaimSet.System)
        {
          // Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
            foreach (Claim c in cs.FindClaims("http://www.contoso.com/claims/allowedoperation", Rights.PossessProperty))
          {
            // If the Claim resource matches the action URI then return true to allow access.
            if (action == c.Resource.ToString())
              return true;
          }
        }
      }
    
      // If this point is reached, return false to deny access.
      return false;
    }
    
    Protected Overrides Function CheckAccessCore(ByVal operationContext As OperationContext) As Boolean
        ' Extract the action URI from the OperationContext. Match this against the claims.
        ' in the AuthorizationContext.
        Dim action As String = operationContext.RequestContext.RequestMessage.Headers.Action
    
        ' Iterate through the various claimsets in the AuthorizationContext.
        Dim cs As ClaimSet
        For Each cs In operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets
            ' Examine only those claim sets issued by System.
            If cs.Issuer Is ClaimSet.System Then
                ' Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
                Dim c As Claim
                For Each c In cs.FindClaims("http://www.contoso.com/claims/allowedoperation", _
                     Rights.PossessProperty)
                    ' If the Claim resource matches the action URI then return true to allow access.
                    If action = c.Resource.ToString() Then
                        Return True
                    End If
                Next c
            End If
        Next cs
        ' If this point is reached, return false to deny access.
        Return False
    
    End Function
    

To register a custom authorization manager using code

  1. Create an instance of the custom authorization manager and assign it to the ServiceAuthorizationManager property.

    The ServiceAuthorizationBehavior can be accessed using Authorization property.

    The following code example registers the MyServiceAuthorizationManager custom authorization manager.

    // Add a custom authorization manager to the service authorization behavior.
    serviceHost.Authorization.ServiceAuthorizationManager =
               new MyServiceAuthorizationManager();
    
    ' Add a custom authorization manager to the service authorization behavior.
    serviceHost.Authorization.ServiceAuthorizationManager = _
        New MyServiceAuthorizationManager()
    
    

To register a custom authorization manager using configuration

  1. Open the configuration file for the service.

  2. Add a <serviceAuthorization> to the <behaviors>.

    To the <serviceAuthorization>, add a serviceAuthorizationManagerType attribute and set its value to the type that represents the custom authorization manager.

  3. Add a binding that secures the communication between the client and service.

    The binding that is chosen for this communication determines the claims that are added to the AuthorizationContext, which the custom authorization manager uses to make authorization decisions. For more details about the system-provided bindings, see System-Provided Bindings.

  4. Associate the behavior to a service endpoint, by adding a <service> element and set the value of the behaviorConfiguration attribute to the value of the name attribute for the <behavior> element.

    For more information about configuring a service endpoint, see How to: Create a Service Endpoint in Configuration.

    The following code example registers the custom authorization manager Samples.MyServiceAuthorizationManager.

    <configuration>
      <system.serviceModel>
        <services>
          <service
              name="Microsoft.ServiceModel.Samples.CalculatorService"
              behaviorConfiguration="CalculatorServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
              </baseAddresses>
            </host>
            <endpoint address=""
                      binding="wsHttpBinding_Calculator"
                      contract="Microsoft.ServiceModel.Samples.ICalculator" />
          </service>
        </services>
        <bindings>
          <WSHttpBinding>
           <binding name = "wsHttpBinding_Calculator">
             <security mode="Message">
               <message clientCredentialType="Windows"/>
             </security>
            </binding>
          </WSHttpBinding>
        </bindings>
        <behaviors>
          <serviceBehaviors>
            <behavior name="CalculatorServiceBehavior">
              <serviceAuthorization serviceAuthorizationManagerType="Samples.MyServiceAuthorizationManager,MyAssembly" />
             </behavior>
         </serviceBehaviors>
       </behaviors>
      </system.serviceModel>
    </configuration>
    

    Warning

    Note that when you specify the serviceAuthorizationManagerType, the string must contain the fully qualified type name. a comma, and the name of the assembly in which the type is defined. If you leave out the assembly name, WCF will attempt to load the type from System.ServiceModel.dll.

Example

The following code example demonstrates a basic implementation of a ServiceAuthorizationManager class that includes overriding the CheckAccessCore method. The example code examines the AuthorizationContext for a custom claim and returns true when the resource for that custom claim matches the action value from the OperationContext. For a more complete implementation of a ServiceAuthorizationManager class, see Authorization Policy.

public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
  protected override bool CheckAccessCore(OperationContext operationContext)
  {
    // Extract the action URI from the OperationContext. Match this against the claims
    // in the AuthorizationContext.
    string action = operationContext.RequestContext.RequestMessage.Headers.Action;
  
    // Iterate through the various claim sets in the AuthorizationContext.
    foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
    {
      // Examine only those claim sets issued by System.
      if (cs.Issuer == ClaimSet.System)
      {
        // Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
          foreach (Claim c in cs.FindClaims("http://www.contoso.com/claims/allowedoperation", Rights.PossessProperty))
        {
          // If the Claim resource matches the action URI then return true to allow access.
          if (action == c.Resource.ToString())
            return true;
        }
      }
    }
  
    // If this point is reached, return false to deny access.
    return false;
  }
}

Public Class MyServiceAuthorizationManager
    Inherits ServiceAuthorizationManager

    Protected Overrides Function CheckAccessCore(ByVal operationContext As OperationContext) As Boolean
        ' Extract the action URI from the OperationContext. Match this against the claims.
        ' in the AuthorizationContext.
        Dim action As String = operationContext.RequestContext.RequestMessage.Headers.Action

        ' Iterate through the various claimsets in the AuthorizationContext.
        Dim cs As ClaimSet
        For Each cs In operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets
            ' Examine only those claim sets issued by System.
            If cs.Issuer Is ClaimSet.System Then
                ' Iterate through claims of type "http://www.contoso.com/claims/allowedoperation".
                Dim c As Claim
                For Each c In cs.FindClaims("http://www.contoso.com/claims/allowedoperation", _
                     Rights.PossessProperty)
                    ' If the Claim resource matches the action URI then return true to allow access.
                    If action = c.Resource.ToString() Then
                        Return True
                    End If
                Next c
            End If
        Next cs
        ' If this point is reached, return false to deny access.
        Return False

    End Function
End Class

See also