Search code examples
c#wcfazureacs

WCF authentication with Azure ACS increase MaxReceivedMessageSize


I have a WCF service that authenticates via Azure ACS, it works beautifully except that when I upload large files to it, I get "(413) Request Entity Too Large"

So clearly I need to increase MaxReceivedMessageSize, however, my binding type isn't a WSHttpBinding but a IssuedTokenWSTrustBinding so doesn't expose this property and I guess I don't fully understand how the HTTP bindings are created in the code below. Is it possible to configure MaxReceivedMessageSize on my bindings somehow?

    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
    {
        string acsUsernameEndpoint = String.Format("https://{0}.{1}/v2/wstrust/13/username", ACSServiceNamespace, AcsHostUrl);
        ServiceHost rpHost = new ServiceHost(typeof(DataTransferService));
        rpHost.Credentials.ServiceCertificate.Certificate = GetServiceCertificateWithPrivateKey();
        rpHost.AddServiceEndpoint(typeof(IUploadService),
                                   Bindings.CreateServiceBinding(acsUsernameEndpoint),
                                   new Uri(ServiceAddress));

        // Windows Identity Foundation token handlers can pick up the relevant settings.
        ServiceConfiguration serviceConfiguration = new ServiceConfiguration();
        // FederatedServiceCredentials.ConfigureServiceHost etc...
        return rpHost;
    }

    public static class Bindings
    {
        public static Binding CreateServiceBinding(string acsUsernameEndpoint)
        {
            return new IssuedTokenWSTrustBinding(CreateAcsUsernameBinding(), new EndpointAddress(acsUsernameEndpoint));
        }

        public static Binding CreateAcsUsernameBinding()
        {
            return new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential);
        }
    }

Solution

  • Solved! As I'm not explicitly defining my endpoints it should be possible to set ReaderQuotas and MaxReceivedMessageSize etc on the 'default' endpoints with .Net 4+ by not naming them, this didn't work.

    In the end I managed to change the ReaderQuotas and MaxReceivedMessageSize on the binding by creating a new Binding class that overrides CreateBindingElements(), this new class takes the binding by the IssuedTokenWSTrustBinding and calls its CreateBindingElements() to create a clone of its binding components, this then allows me to tweak the settings. I'm left with a completely code-based WCF configuration, not sure I like the .clone() - there might be a more eloquent solution.

    private class LargeTransportBinding : Binding
    {
        Binding originalBinding;
    
        public LargeTransportBinding(Binding sourceBinding)
        {
            originalBinding = sourceBinding;
        }
    
        public override BindingElementCollection CreateBindingElements()
        {
            // Copy
            BindingElementCollection modifiedBindingElementCollection = originalBinding.CreateBindingElements().Clone();
    
            // Tweak Reader Quoters and max buffer sizes
            TextMessageEncodingBindingElement encoding = (TextMessageEncodingBindingElement)modifiedBindingElementCollection[1];
            encoding.ReaderQuotas.MaxArrayLength = int.MaxValue;
            encoding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
            encoding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
    
            HttpTransportBindingElement transport = (HttpTransportBindingElement)modifiedBindingElementCollection[2];
            transport.MaxBufferPoolSize = int.MaxValue;
            transport.MaxBufferSize = int.MaxValue;
            transport.MaxReceivedMessageSize = int.MaxValue;
    
            return modifiedBindingElementCollection;
        }
    
        public override string Scheme
        {
            get { return originalBinding.Scheme; }
        }
    }
    

    and calling it as follows:

        var binding =  Bindings.CreateServiceBinding(acsUsernameEndpoint);
        rpHost.AddServiceEndpoint(typeof(IUploadService),
                         new LargeTransportBinding(binding),
                         new Uri(ServiceAddress));
    

    Remembering to reduce the Int.MaxValue to something more realistic to reduce the chance of a DoS attack. I hope this saves somebody the many days I had to spend trawling the internet to solve this.

    For IIS 7, this is also required in the web.config:

      <system.web>
        <httpRuntime maxRequestLength="10240" />
      </system.web>