8

I have a basic ASMX service that I'm trying to get running (I'd rather use WCF but can't get the server to work with it). It runs fine in a no security setup but as soon as I turn on security I get:

The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm="Secured area"'.

What I want is a minimalistic ask the user for a name and password type solution.

Pokeing around the code with intellisense doesn't come up with anything that looks like I need.

This looks like it might be useful but it seems to be WCF so who knows.


I just realized I can make this a live demo:

here is the service: http://smplsite.com/sandbox3/Service1.asmx

the username is testapp and the password is testpw. I need a command line app that calls functions on that service.

Befor I added security, this line worked in a basic VS project after running Add Web Service Reference on that URL

new ServiceReference1.Service1SoapClient().HelloMom("Bob");

This is my current attempt (That doesn't work)

class Program
{
    private static bool customValidation(object s, X509Certificate c, X509Chain ch, SslPolicyErrors e)
    { return true }

    static void Main(string[] args)
    {
         // accept anything
        ServicePointManager.ServerCertificateValidationCallback += 
              new RemoteCertificateValidationCallback(customValidation);

        var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
        binding.Security.Transport.Realm = "Secured area";

        // the generated Web Service Reference class
        var client = new ServiceReference1.Service1SoapClient(
            binding,
            new EndpointAddress("https://smplsite.com/sandbox3/Service1.asmx")
            );

        client.ClientCredentials.UserName.UserName = "testapp";
        client.ClientCredentials.UserName.Password = "testpw";

        Console.WriteLine(client.HelloMom("Bob"));
    }
}

Edit: BTW this is not a website or running in the browser, the accessing code is a C# command line app. Also, the authentication is being done by another IIS plug-in that I don't control.

Edit 2: To be clear; the solution I'm looking for is a purely client side issue.

Edit 3: the access control is via a .haccess type of system and I like it that way. I don't want the service code to do any authentication.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
BCS
  • 75,627
  • 68
  • 187
  • 294
  • After reading your edit, I'm not sure my solution will help, bcause of the other IIS plugin doing the authentication. I'll leave it there in case it lights a spark... – Moose Apr 20 '09 at 23:06
  • I've edited my answer. I don't know if that will help, but it's another attempt... – Moose Apr 20 '09 at 23:16
  • can you get to your web service using a browser, and watch what's being sent to it using fiddler? Then you can attempt to duplicate the authentication that the browser uses. Otherwise, I'm out of ideas. – Moose Apr 21 '09 at 02:19
  • To complicated, I'm not in a position to do something that hacked together – BCS Apr 21 '09 at 16:20
  • I can't get in using that username/pwd. However, looking at the headers, it appears to be just looking for basic authentication. I can't prove that though. – Moose Apr 22 '09 at 14:16
  • Can you get in via a web browser? – BCS Apr 22 '09 at 18:30
  • I tried hitting it with IE while Fiddler was watching, it acts like the password is bad. Returns a 401.1. – Moose Apr 22 '09 at 20:11

3 Answers3

12

Edit:
How about using this:

MyWebService svc = new MyWebService();            
svc.Credentials = new System.Net.NetworkCredential(UserID, pwd);
bool result = svc.MyWebMethod();    

OP says this wouldn't work, and now I see that it wouldn't in his situation.

We do something like this:

public class MyWebService : System.Web.Services.WebService
{
    public AuthenticationHeader AuthenticationInformation;

    public class AuthenticationHeader : SoapHeader
    {
        public string UserName;
        public string Password;
    }

    [WebMethod( Description = "Sample WebMethod." )]
    [SoapHeader( "AuthenticationInformation" )]
    public bool MyWebMethod()
    {
        if ( AuthenticationInformation != null )
        {
            if ( IsUserAuthenticated( AuthenticationInformation.UserName,   
                 AuthenticationInformation.Password, ref errorMessage ) )
            {
                 // Authenticated, do something
            }
            else
            {
                 // Failed Authentication, do something
            } 
        }
        else
        {
                 // No Authentication, do something
        }
    }
}

Note that you supply IsUserAuthenticated().

Then the client calls it like this:

 MyWebService svc = new MyWebService();            
 svc.AuthenticationHeaderValue = new MyWebService.AuthenticationHeader();
 svc.AuthenticationHeaderValue.UserName = UserID;
 svc.AuthenticationHeaderValue.Password = Password;

 bool result = svc.MyWebMethod();
Moose
  • 5,354
  • 3
  • 33
  • 46
  • No, sorry that doesn't do what I need: as far as I can tell, IIS is rejecting the access attempt before and of my code even start to run. I think (and hope) it is using the same type of mechanism it would use for access control with static content. – BCS Apr 20 '09 at 23:12
  • Okay, then what about using the Credentials Property off your web service object? – Moose Apr 20 '09 at 23:13
  • Thanks, this AuthenticationHeader idea worked well for me. The only thing is that I couldn't get access to `svc.AuthenticationHeaderValue`. I had to end up calling `result = svc.MyWebMethod(authHeader);`. – deanis Aug 17 '12 at 03:10
2

Config:

<binding name="MyBinding" closeTimeout="00:00:30"
      openTimeout="00:00:30" receiveTimeout="00:00:30" sendTimeout="00:00:30"
      allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="524288" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
      textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true"
      messageEncoding="Text">
      <readerQuotas maxDepth="32" maxStringContentLength="2147483647"
        maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      <security mode="Transport">
        <transport clientCredentialType="Basic" realm=""/>
      </security>
    </binding>
  </basicHttpBinding>

While Consuming

proxy.ClientCredentials.UserName.UserName = userName;
proxy.ClientCredentials.UserName.Password = password;

This worked out just fine for me.

Chandan Kumar
  • 4,570
  • 4
  • 42
  • 62
2

I'm going to add a fresh answer, because I think this may help:

http://intellitect.com/calling-web-services-using-basic-authentication/

I won't duplicate it here, because I didn't do anything but google it.

Alex Pokislyuk
  • 213
  • 3
  • 8
Moose
  • 5,354
  • 3
  • 33
  • 46
  • Sounds promising but there doesn't seem to be a GetWebRequest method to override :( – BCS Apr 22 '09 at 18:39
  • I can't see your Web Service to look inside your classes, but I took a look at one I use here. Its base class is System.Web.Services.Protocols.SoapHttpClientProtocol, which has a protected override System.Net.WebRequest GetWebRequest(System.Uri uri). This is the one you'd need to override, isn't it? – Moose Apr 22 '09 at 20:17