Wcf: Basic Authentication in Asp.net Core

Created on 20 Mar 2017  路  8Comments  路  Source: dotnet/wcf

I created a Asmx Web service and host it in IIS, in MVC, I could call it from below code:
BasicWebService.WebService1 client = new BasicWebService.WebService1(); client.Credentials = new System.Net.NetworkCredential("xx", "xx","xx"); string result = client.HelloWorld();
In Asp.net Core, I tried WCF Connected Service Visual Studio Extension, and it generate the client code correctly. But I failed to call the web service method due to that unauthenticated.
I tried a lot of ways, but nothing works.
` ServiceReference1.WebService1SoapClient client = new ServiceReference1.WebService1SoapClient(ServiceReference1.WebService1SoapClient.EndpointConfiguration.WebService1Soap);
client.ClientCredentials.UserName.UserName = "xx";
client.ClientCredentials.UserName.Password = "xx";

        //string USER = "xx";
        //string PASSWORD = "xx";
        //string Domain = "xx";
        //NetworkCredential netCredential = new NetworkCredential(USER, PASSWORD,Domain);
        ////client.Credentials = new System.Net.NetworkCredential("xx", "xx", "xx");
        //client.ClientCredentials.Windows.ClientCredential = netCredential;// netCredential.GetCredential(new Uri("http://localhost/WCFBasicSecurity/WebService1.asmx"), "Basic");
        ServiceReference1.HelloWorldResponse result =client.HelloWorldAsync().Result;`.

In addition, I could inherit generated client code in mvc, but it does not exist in Asp.net Core generated code.
` public class WebServiceEx:BasicWebService.WebService1
{
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{

        HttpWebRequest request;
        request = (HttpWebRequest)base.GetWebRequest(uri);

        //if (PreAuthenticate)
        //{
        //NetworkCredential networkCredentials = Credentials.GetCredential(uri, "Basic");
        //if (networkCredentials != null)
        //{

        //    byte[] credentialBuffer = new UTF8Encoding().GetBytes(networkCredentials.UserName + ":" + networkCredentials.Password);

        //    request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(credentialBuffer);
        //}
        //else
        //{
        //    throw new ApplicationException("No network credentials");
        //}
        //}
        byte[] credentialBuffer = new UTF8Encoding().GetBytes("xx"+ ":" + "xx");

        request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(credentialBuffer);
        return request;
    }

}`

Most helpful comment

Assume you use EndpointConfiguration.WebService1Soap, you need to change following code, I bolded the lines need modification:
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding (System.ServiceModel.BasicHttpSecurityMode.Transport);
result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Basic;

result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}

public WebService1SoapClient(EndpointConfiguration endpointConfiguration) :
base(WebService1SoapClient.GetBindingForEndpoint(endpointConfiguration), WebService1SoapClient.GetEndpointAddress(endpointConfiguration))
{
this.Endpoint.Name = endpointConfiguration.ToString();
this.ChannelFactory.Credentials.UserName.UserName = "yourusername";
this.ChannelFactory.Credentials.UserName.Password = "yourpassword"
;
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}

Do you need to use EndpointConfiguration.WebService1Soap12?

Thanks,
Hong

All 8 comments

@Edward-Zhou Sorry for running into this and thanks for reporting the issue.
WCF Connected Service does not support service metadata secured by authentication yet. As a workaround, could you please try to download the metadata into a file from a web browser (use singleWsdl if you have modular WSDL in multiple files), then restart the WCF Connected Service and enter the full path to the downloaded file in the URI box?

@hongdai Thanks for your feedback. But I think the issue is not the service metadata. I secure asmx web service by IIS Basic Authentication, and I could generate client code by enabling Anonymous Authentication in IIS which could get the same result by generating client code by local wsdl file.
But, we need to provide user name and password when we access the service if we only enable Basic Authentication. I failed to provide the authentication information when I call the asmx service method.

@Edward-Zhou You are right it's not the service metadata as you already work around the issue by enabling Anonymous Authentication.

WCF Connected Service Visual Studio Extension does not yet support generating authenticated client code. However there should be a workaround as WCF runtime already supports it. Could you send me the generated reference.cs? I'll give a try to help you add basic authentication.

@hongdai Thanks very much.
Here is the reference.cs.
`namespace ServiceReference3
{

[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference3.WebService1Soap")]
public interface WebService1Soap
{

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/HelloWorld", ReplyAction="*")]
    System.Threading.Tasks.Task<ServiceReference3.HelloWorldResponse> HelloWorldAsync(ServiceReference3.HelloWorldRequest request);
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
public partial class HelloWorldRequest
{

    [System.ServiceModel.MessageBodyMemberAttribute(Name="HelloWorld", Namespace="http://tempuri.org/", Order=0)]
    public ServiceReference3.HelloWorldRequestBody Body;

    public HelloWorldRequest()
    {
    }

    public HelloWorldRequest(ServiceReference3.HelloWorldRequestBody Body)
    {
        this.Body = Body;
    }
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.Runtime.Serialization.DataContractAttribute()]
public partial class HelloWorldRequestBody
{

    public HelloWorldRequestBody()
    {
    }
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(IsWrapped=false)]
public partial class HelloWorldResponse
{

    [System.ServiceModel.MessageBodyMemberAttribute(Name="HelloWorldResponse", Namespace="http://tempuri.org/", Order=0)]
    public ServiceReference3.HelloWorldResponseBody Body;

    public HelloWorldResponse()
    {
    }

    public HelloWorldResponse(ServiceReference3.HelloWorldResponseBody Body)
    {
        this.Body = Body;
    }
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.Runtime.Serialization.DataContractAttribute(Namespace="http://tempuri.org/")]
public partial class HelloWorldResponseBody
{

    [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=0)]
    public string HelloWorldResult;

    public HelloWorldResponseBody()
    {
    }

    public HelloWorldResponseBody(string HelloWorldResult)
    {
        this.HelloWorldResult = HelloWorldResult;
    }
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
public interface WebService1SoapChannel : ServiceReference3.WebService1Soap, System.ServiceModel.IClientChannel
{
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "0.4.0.0")]
public partial class WebService1SoapClient : System.ServiceModel.ClientBase<ServiceReference3.WebService1Soap>, ServiceReference3.WebService1Soap
{

/// <summary>
/// Implement this partial method to configure the service endpoint.
/// </summary>
/// <param name="serviceEndpoint">The endpoint to configure</param>
/// <param name="clientCredentials">The client credentials</param>
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);

    public WebService1SoapClient(EndpointConfiguration endpointConfiguration) : 
            base(WebService1SoapClient.GetBindingForEndpoint(endpointConfiguration), WebService1SoapClient.GetEndpointAddress(endpointConfiguration))
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }

    public WebService1SoapClient(EndpointConfiguration endpointConfiguration, string remoteAddress) : 
            base(WebService1SoapClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }

    public WebService1SoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(WebService1SoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }

    public WebService1SoapClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress)
    {
    }

    [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
    System.Threading.Tasks.Task<ServiceReference3.HelloWorldResponse> ServiceReference3.WebService1Soap.HelloWorldAsync(ServiceReference3.HelloWorldRequest request)
    {
        return base.Channel.HelloWorldAsync(request);
    }

    public System.Threading.Tasks.Task<ServiceReference3.HelloWorldResponse> HelloWorldAsync()
    {
        ServiceReference3.HelloWorldRequest inValue = new ServiceReference3.HelloWorldRequest();
        inValue.Body = new ServiceReference3.HelloWorldRequestBody();
        return ((ServiceReference3.WebService1Soap)(this)).HelloWorldAsync(inValue);
    }

    public virtual System.Threading.Tasks.Task OpenAsync()
    {
        return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
    }

    public virtual System.Threading.Tasks.Task CloseAsync()
    {
        return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginClose(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndClose));
    }

    private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
        {
            System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
            result.MaxBufferSize = int.MaxValue;
            result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
            result.MaxReceivedMessageSize = int.MaxValue;
            result.AllowCookies = true;
            return result;
        }
        if ((endpointConfiguration == EndpointConfiguration.WebService1Soap12))
        {
            System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
            System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
            textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.None);
            result.Elements.Add(textBindingElement);
            System.ServiceModel.Channels.HttpTransportBindingElement httpBindingElement = new System.ServiceModel.Channels.HttpTransportBindingElement();
            httpBindingElement.AllowCookies = true;
            httpBindingElement.MaxBufferSize = int.MaxValue;
            httpBindingElement.MaxReceivedMessageSize = int.MaxValue;
            result.Elements.Add(httpBindingElement);
            return result;
        }
        throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
    }

    private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
        {
            return new System.ServiceModel.EndpointAddress("http://localhost/WCFBasicSecurity/WebService1.asmx");
        }
        if ((endpointConfiguration == EndpointConfiguration.WebService1Soap12))
        {
            return new System.ServiceModel.EndpointAddress("http://localhost/WCFBasicSecurity/WebService1.asmx");
        }
        throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
    }

    public enum EndpointConfiguration
    {

        WebService1Soap,

        WebService1Soap12,
    }
}

}
`

Assume you use EndpointConfiguration.WebService1Soap, you need to change following code, I bolded the lines need modification:
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding (System.ServiceModel.BasicHttpSecurityMode.Transport);
result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Basic;

result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}

public WebService1SoapClient(EndpointConfiguration endpointConfiguration) :
base(WebService1SoapClient.GetBindingForEndpoint(endpointConfiguration), WebService1SoapClient.GetEndpointAddress(endpointConfiguration))
{
this.Endpoint.Name = endpointConfiguration.ToString();
this.ChannelFactory.Credentials.UserName.UserName = "yourusername";
this.ChannelFactory.Credentials.UserName.Password = "yourpassword"
;
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}

Do you need to use EndpointConfiguration.WebService1Soap12?

Thanks,
Hong

@hongdai Thanks a lot, it works by changing to Https url under your code.

private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
        {
            if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
            {
                return new System.ServiceModel.EndpointAddress("https://localhost/WCFBasicSecurity/WebService1.asmx");
                //return new System.ServiceModel.EndpointAddress("http://localhost/WCFBasicSecurity/WebService1.asmx");
            }
            if ((endpointConfiguration == EndpointConfiguration.WebService1Soap12))
            {
                return new System.ServiceModel.EndpointAddress("http://localhost/WCFBasicSecurity/WebService1.asmx");
            }
            throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
        }

I am wondering whether it is possible under http url.

Yes. You should be able to use http url by changing:

System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding (System.ServiceModel.BasicHttpSecurityMode.Transport);

to
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding (System.ServiceModel.BasicHttpSecurityMode.TransportCredentialOnly);

Perfectly. Thanks a lot.

Was this page helpful?
0 / 5 - 0 ratings