Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't read headers in WCF service

I am trying to add a custom security feature where the client adds a token to each service call made by a Silverlight client and then the service can access this token to control an application security system. I am attempting to do this by implementing the IClientMessageInspector interface and linking this in to my generated service client. I am not using the Visual studio generated proxy but my own client created by a ChannelFactory. I have managed to find several solutions on the web which all seem to use one of 2 basic methods. Firstly by adding a header to the headers collection of the Message presented to the IClientMessageInspector.BeforeSendRequest, and secondly by using an HttpRequestMessageProperty to add the info as a property to the Message presented to the IClientMessageInspector.BeforeSendRequest. I have been trying both of these methods without any success. It appears that both techniques successfully add data to the request but I have been unable to access either at the server. I would add that this is a very new area for me and it is very possible that I have missed the answer on the internet because of inexperience.

The code to generate my client is:

    private ISecurityAdministrationContract CreateChannel()
    {
        if (factory == null)
        {
            lock (this)
            {
                // Create a custom binding that uses HTTP and binary encoding.
                var elements = new List<BindingElement>();
                elements.Add(new BinaryMessageEncodingBindingElement());
                elements.Add(new HttpTransportBindingElement());
                var binding = new CustomBinding(elements);

                // Create a channel factory for the service endpoint configured with the custom binding.
                factory = new ChannelFactory<ISecurityAdministrationContract>(binding, new EndpointAddress(SecurityAdminServiceAddress));

                //Add my IClientMessageInspector
                factory.Endpoint.Behaviors.Add(new ClientSecurityInterceptor());
            }                
        }
        ISecurityAdministrationContract client = factory.CreateChannel();
        return client;
    }
        }
        ISecurityAdministrationContract client = factory.CreateChannel();
        return client;
    }

The code to add my header to the request is:

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        //Method 1
        MessageHeader header = MessageHeader.CreateHeader("MyFirstAuthentication", "ns", "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEEE");
        request.Headers.Add(header);

        //Method 2
        HttpRequestMessageProperty httpRequestMessage;
        httpRequestMessage = new HttpRequestMessageProperty();
        httpRequestMessage.Headers["MySecondAuthentication"] = "11111111-2222-3333-4444-5555555555555";
        request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);

        return null;
    }

The above code implements both techniques.

The service call trapped by fiddler is: (NB http replaced with ht_tp cos the site won't allow me to post hyoerlinks)

POST ht_tp://127.0.0.1:6785/SecurityAdministrationRelayService.svc/ HTTP/1.1 Accept: / Referer: ht_tp://ipv4.fiddler:6785/ClientBin/Civica.Housing.xap Accept-Language: en-gb Content-Length: 400 Content-Type: application/soap+msbin1 MySecondAuthentication: 11111111-2222-3333-4444-5555555555555 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB5; InfoPath.2; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727) Host: 127.0.0.1:6785 Connection: Keep-Alive Pragma: no-cache

V_ s _a_V_D ���Ahttp://tempuri.org/ISecurityAdministrationContract/CreateAdvocateD�ߔ_�9�HJ��9��-��D,D*�@MyFirstAuthentication_ns�%AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEEED _���@http://ipv4.fiddler:6785/SecurityAdministrationRelayService.svc/V@_CreateAdvocate_http://tempuri.org/@ advocateVO�{"Id":0,"UserId":4,"AdvocateForId":8,"ValidFrom":"/Date(1291127869690+0000)/","ValidTo":null}__

This does seem to contain the token info for both of the techniques.

The problem comes at the server when I try to extract this info. I have implemented an IOperationInvoker and tried to find the header in the IncomingMessageHeaders without any success. I have also searched in the IncomingMessageProperties but I cannot see any of my added header detail. This is the code I am using in the IOperationInvoker:

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        PreInvoke(instance, inputs);
        object returnedValue = null;
        object[] outputparams = new object[] { };
        Exception exception = null;
        try
        {
            returnedValue = originalInvoker.Invoke(instance, inputs, out outputparams);
            outputs = outputparams;
            return returnedValue;
        }
        catch (Exception e)
        {
            exception = e; throw;
        }
        finally
        {
            PostInvoke(instance, returnedValue, outputparams, exception);
        }
    }


    protected virtual void PreInvoke(object instance, object[] inputs)
    {
        //Look for header directly
        int index = System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.FindHeader("MyFirstAuthentication", "ns");

        //Search via enumerator
        foreach (var header in System.ServiceModel.OperationContext.Current.IncomingMessageHeaders)
        {
        }

    }

The FindHeader returns -1 whilst the enumerator finds 5 headers, namely; ‘Action’, ‘MessageID’, ‘ReplyTo’, ‘VsDebuggerCausalityData’ & ‘To’.

The OperationContext.Current.IncomingMessageProperties collection contains 4 entries, namely: ‘Via’, ‘Security’, ‘Encoder’ & ‘System.ServiceModel.Channels.RemoteEndpointMessageProperty’

In fact if I comment out the line in my client that adds the IClientMessageInspector to the ClienChannel then the http that fiddler reports changes by omitting the added detail, but the header and property collections on the Incoming message are unchanged.

Any ideas as to how I can access this info, or why it is not being presented as part of the IncomingMessage would be very gratefully received.

like image 268
Roger Adams Avatar asked Nov 30 '10 14:11

Roger Adams


2 Answers

I've answered my own question. There is nothing wrong with any of the above code, it just needs to be linked into the correct place. In my full solution I am calling a service from Silverlight client which then calls a second service. Embarrassingly I had linked my IOperationInvoker into the second service(doh). When I linked it in to the intermediate service the header was available as expected.

The code to read the value is:

            int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("MyFirstAuthentication", "ns");
            if (index >= 0)
            {
                string value = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index);
            }
like image 154
Roger Adams Avatar answered Nov 12 '22 01:11

Roger Adams


You can use the following to access a header value:

var headers = OperationContext.Current.IncomingMessageProperties["httpRequest"];
var apiToken = ((HttpRequestMessageProperty)headers).Headers["<YourHeaderKey>"];
like image 26
Taran Avatar answered Nov 12 '22 02:11

Taran