17

I'm working on custom WCF authentication and authorization and found some articles about UserNamePasswordValidator and ServiceAuthorizationManager.

I also found clues about using a custom System.ServiceModel.ServiceAuthenticationManager (dead link ), but msdn does not tell a lot about it ( http://msdn.microsoft.com/en-us/library/system.servicemodel.serviceauthenticationmanager.aspx ).

So here I am: anyone knows more about ServiceAuthenticationManager ?

In general, how would you set up custom WCF authentication ?

H H
  • 263,252
  • 30
  • 330
  • 514
fredlegrain
  • 584
  • 1
  • 6
  • 20
  • 5
    It's weird, but there still there seems to be a lot of info and samples for ServiceAuthorizationManager, but hardly anything for ServiceAuthenticationManager – Cocowalla Aug 04 '11 at 19:44

1 Answers1

38

You're right, the documentation on this is no help at all.

The way I have used this class is as follows. Override the Authenticate() method to:

  1. Pull the authentication tokens (e.g. username/password) out of the incoming message
  2. Authenticate the tokens and use them to create an IPrincipal object. This will be the principal that is used during the invocation of the service operation.
  3. Add the IPrincipal object to the message.Properties collection so it can be used later in the WCF processing pipeline

You can't just set the thread principal at this point as it is changed later on by WCF.

The code in the ServiceAuthenticationManager.Authenticate() methods would look something like this:

public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri, ref Message message)
{
   int tokenPosition = message.Headers.FindHeader("Token", "http://customnamespace.org");
   string token = message.Headers.GetHeader<string>(tokenPosition);

   IPrincipal user = new CustomPrincipal(token);

   message.Properties["Principal"] = user;

   return authPolicy;
}

Then you add a custom authorization policy that

  1. Retrieves the IPrincipal from the message (using the System.ServiceModel.EvaluationContext.Current.IncomingMessageProperties collection).
  2. Pushes the IPrincipal into the EvaluationContext.Properties collection
  3. Makes claims based on the IPrincipal.IsInRole() method

The code in the IAuthorizationPolicy() method would look like

public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
    IPrincipal user = OperationContext.Current.IncomingMessageProperties["Principal"] as IPrincipal;
    evaluationContext.Properties["Principal"] = user;
    evaluationContext.Properties["Identities"] = new List<IIdentity> { user.Identity };

    IList<Claim> roleClaims = this.GetRoleClaims(user);

    evaluationContext.AddClaimSet(this, new DefaultClaimSet(this.Issuer, roleClaims));

    return true;
}

In the service behaviour configuration, you need to set principalPermissionMode="Custom" in order for WCF to set the IPrincipal as the principal on the executing thread for the actual service operation invocation.

<serviceAuthorization principalPermissionMode="Custom"...
Mike Goodwin
  • 8,810
  • 2
  • 35
  • 50
  • Is it possible to add an identity Claim to the ClaimSet in ServiceAuthenticationManager.Authenticate() rather than putting the Principal into the Properties? – Greg Bacchus Sep 05 '11 at 22:15
  • 1
    It is possible. The intended flow I think is that the AuthenticationManager validates the credentials and makes a principal with the claims that come directly from the identity provider (an identity claim would be one of these), the AuthorizationPolicy transforms the claims and the AuthorizationManager makes the authorisation decision. The documentation on this is scarce though so it's hard to tell. Anyway, now that WIF is available the model is simpler :o) – Mike Goodwin Oct 11 '11 at 19:25
  • Thanks so much for the example here Mike but I'm still struggling with this. The examples I've found of ServiceAuthenticationManager all gloss over the "pulling username and password out of headers". It seems I shouldn't have to worry about parsing the XML but can't figure out how to avoid it. It's possible to set a custom `UserNamePasswordValidator` and override `Validate(username, password)` but that gets called _before_ the ServiceAuthenticationManager. I simply want callers to be able to set `ClientCredentials`; is this possible? Or is your `GetHeader` call for custom header logic only? – Tobias J Dec 16 '11 at 17:18
  • 3
    @TobyJ: If you just want to use a username and password set using the ClientCredentials, you should indeed use a custom UserNamePasswordValidator. As you said, the solution I described is for custom headers. In my case I was working with a legacy service that had the username and password as custom message headers, rather than as part of the HTTP or SOAP standard headers. Sorry for not making that clear... – Mike Goodwin Dec 16 '11 at 20:59