How to authorize a WCF service in the anonymous internet

Sometimes I feel like in the time of exploration where I’m a discoverer … But than I always realize that I’m only a developer who tries to understand the mysteries of the Microsoft Framework …

Introduction:
I had to program an authorization mechanism between a client application and a webservice that is hosted by a internet provider. In such a case, we have two different domains in place, no windows integrated authentication, no certificate or something else. The old fashioned way in that case is to use a SoapHeader in order to send informations about the user to the web service … That’s the point where the trail starts …

SoapHeader in WCF Services:
The new Microsoft Technology is not in love with SoapHeaders. Actually it does not know them. But fortunately there is a project on CodePlex called “WCFExtras” which re-implemented the [SoapHeader] Attribute for WCF. So I created an Authentication class in the following way.

[DataContract(Name="AuthenticationHeader")]
public class AuthenticationHeader
{
    [DataMember]
    public string TpUser { get; set; }
    [DataMember]
    public string TpPwdMd5 { get; set; }
    
    [DataMember]
    public string Culture { get; set; }
}

.. And tagged my methods within the [ServiceContract] with the new [SoapHeader] attribute.

[OperationContract]
[SoapHeader("Authentication", typeof(AuthenticationHeader))]
UserStub CreateUser(string logonName);

In order to use the [SoapHeader] attribute, I had to references the WCFExtras.dll in my project and within my web.config file in the behaviorExtensions.

<behaviors>
<endpointBehaviors>
<behavior name="UserServiceEndpointBehaviors">
<wsdlExtensions/>
</behavior>
</endpointBehaviors>
</behaviors>

<extensions>
<behaviorExtensions>
<!– Declare that we have an extension called WSDL Extras–>
<add name="wsdlExtensions" type="WCFExtras.Wsdl.WsdlExtensionsConfig, WCFExtras, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null;/>
</behaviorExtensions>
</extensions>

At server side everything had been fine now.

How to add the SoapHeader at the client side:
But on the client side, I had to send the SoapHeader over the wire whenever a method will be called. For that you have to know, that a SoapHeader is bound to the EndpointAddress of the WCF Service. The binding on the EndpointAddress can be done when creating the WebService via the ChannelFactory.

var authentication = new Dao.Core.TpAuthentication
{
    TpUser = connection.UserName,
    TpPwdMd5 = Md5Crypt.ComputeHash(connection.UserPassword),
    Culture = Thread.CurrentThread.CurrentUICulture.Name
};

var authenticationHeader = AddressHeader.CreateAddressHeader("Authentication", "", authentication);
var serviceUri = new Uri("http://localhost:81/tphost/UserService.svc&quot;));

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.None);
var endpointAddress = new EndpointAddress(serviceUri, new [] { authenticationHeader});
wst = new ChannelFactory<WebServiceTemplate>(basicHttpBinding, endpointAddress).CreateChannel();

At this point in the service host I was able to read user information which have been sent, by the client using the SoapHeader.

But how could I manage the Authentication in my service host?
It was my goal to use declarative and imperative authentication by using the Thread.CurrentPrincipal.IsInRole functionality. In order to use this functionality I had to create my own implementation of IPrincipal and IIdentity. I assume that you know how to implement this interfaces, because the implementation is mainly driven by your business logic. That’s why I jump directly to the interessting part, the AuthorizationPolicy. At server side I had to create a new class that inherits the IAuthorizationPolicy interface.

public class AuthorisationPolicy : IAuthorizationPolicy
{
    string id = Guid.NewGuid().ToString();
    
    public string Id
    {
        get { return id; }
    }
    
    public ClaimSet Issuer
    {
        get { return ClaimSet.System; }
    }
    
    public bool Evaluate(EvaluationContext context, ref object state)
    {
        // Extract the authentication
        var authentication = SoapHeaderHelper<Authentication>.GetInputHeader("Authentication");
        
        …
        
        context.Properties["Principal"] = new CustomPrincipal(webServiceUser, new CustomIdentity(webServiceUser));
        
        return true;
    }
}

The interface has only three methods. The property Id is only a unique identifier of your AuthorizationPolicy – so I did not care about. The Claimset Issuer is only used to define, if the System claims the authorization, or if Windows itself claims it. In my case – it’s always the System. The real magic is implemented within the Evalute method.

The first method reads the SoapHeader that contains the user information.

var authentication = SoapHeaderHelper<Authentication>.GetInputHeader("Authentication");

With this information I had been be able to authenticate the user. If the authentication fails, the method must return false. If not, a new Principal must be set into the context.Properties["Principal"]. The key “Principal” is pre-defined by Microsoft and the new content will instantly placed into the Thread.CurrentPrincipal.

So, that’s the magic.

But who calls the AuthorizationPolicy?

In order to make the new AuthorizationPolicy available to the WCF Framework, I had to to publish it in my [ServiceBehaviors] within the web.config file.

<serviceBehaviors>
<behavior name="UserServiceBehaviors" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="WebServices.Core.AuthorisationPolicy, WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>

So, that’s it. Knowing this, you are also able to write your own authorization process, in case you need it.

So enjoy this piece of code
– Gerhard

kick it on DotNetKicks.com

About these ads

One Response to “How to authorize a WCF service in the anonymous internet”

  1. referencement google Says:

    I’m not that much of a online reader to be honest but your sites really nice,
    keep it up! I’ll go ahead and bookmark your
    site to come back later. All the best


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 106 other followers

%d bloggers like this: