David Lance

Solutions For Adobe's Idiosyncrasies

Archive for the category “Web Service”

Web Service BasicAuthentication Solution

I’ve shown how to add the Authorization header to your client. Now, lets take it server side. How do we receive that header and either allow access or reject the request with a SoapException. Lets start at the authentication piece. This object will be instantiated from the endpoint method and references a remote ActiveDirectory LDAP. I also happen to be using log4net as my logging solution. Cut that part out if not needed.

namespace
{
    public class BasicAuthenticator
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(BasicAuthenticator));
        private static string searchString = "OU= ...";
        HttpContext context;
        LdapConnection ldapConn = null;

        public BasicAuthenticator(HttpContext context)
        {
            this.context = context;

            string server1 = ConfigurationManager.AppSettings["LDAP_Server1"];
            string server2 = ConfigurationManager.AppSettings["LDAP_Server2"];
            string port = ConfigurationManager.AppSettings["LDAP_PORT"];
            LdapDirectoryIdentifier ldapIdentity = new LdapDirectoryIdentifier(new string[] { server1, server2 }, Convert.ToInt32(port), false, false);
            ldapConn = new LdapConnection(ldapIdentity);
        }

        public bool authenticate()
        {
            bool authentic = false;

            string authStr = context.Request.Headers["Authorization"];

            if (authStr == null || authStr.Length == 0)
                return false;

            authStr = authStr.Trim();

            string encodedCredentials = authStr.Substring(6);

            byte[] decodedBytes = Convert.FromBase64String(encodedCredentials);
            string s = new ASCIIEncoding().GetString(decodedBytes);

            string[] userPass = s.Split(new char[] { ':' });
            string username = userPass[0];
            string password = userPass[1];

            log.Info("Authentication Attempt: " + username);

            if (TryAuth(context.ApplicationInstance, username, password))
            {
                context.User = new GenericPrincipal(new GenericIdentity(username, "ImporterWS.Basic"), null);
                authentic = true;
            }

            return authentic;
        }

        private bool TryAuth(HttpApplication app, string username, string password)
        {
            bool authentic = false;
            try
            {
                ldapConn.Credential = new NetworkCredential(username, password);

                DirectoryEntry entry = new DirectoryEntry("LDAP://" + searchString, username, password);
                object nativeObject = entry.NativeObject;
                authentic = true;
            }
            catch (DirectoryServicesCOMException dsce)
            {
                log.Warn(dsce.Message);
            }

            log.Debug("Authentic: " + authentic);
            return authentic;
        }
    }
}

 


[WebMethod]

public string HelloWorld()

{
    Authenticator authenticator = new Authenticator(Context);
    if (authenticator.authenticate())
    {
        return "Hello World!";
    }
    else
    {
        throw new SoapException("Authentication Failure", SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri, node);
    }
}

Add BasicAuthentication To Your Web Service Client

Building on my last post using the wsdl.exe app to build a web service client proxy, we now need to add authentication. A common practice is to send your credentials using BasicAuthentication (over HTTPS of course).

All over the web you’ll find simple examples showing you how to set the Credentials object on your proxy and voila it works. That’s great if you have a webservice that challenges you and gives your a client a chance to respond. Unfortunately though, web services typically are stateless, one time calls. There’s no chance for “hey, how ya doin?”. Here’s my solution for adding the Authorization header to the outgoing request (the first outgoing request).

You’ll still use the Credentials object in your client.

WSProxy proxy = new WSProxy();
proxy.Credentials = new NetworkCredential(username, password);
string response = proxy.HelloWorld();

The proxy object will use the Credentials object you just set if you insert this code into the WSProxy object.

/// <summary>
/// Manually insert HTTP Authorization header for Basic Authentication.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
protected override WebRequest GetWebRequest(Uri uri)
{
    string username = ((NetworkCredential)Credentials).UserName;
    string password = ((NetworkCredential)Credentials).Password;
    HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
    string creds = String.Format("{0}:{1}", username, password);
    byte[] bytes = Encoding.ASCII.GetBytes(cre);
    string base64 = Convert.ToBase64String(bytes);
    request.Headers.Add("Authorization", "basic " + base64);
    return request;
}

WSDL.exe vs. Service Reference

The first time I tried to write a web service client in Visual Studio 2010, my instinct said “create a service reference”. After all, Microsoft loves to tie things in through references. So I did… and I quickly realized I was going to have to create a service reference for each environment if I was going to use the same client for accessing endpoints in multiple environments.

A better way to go is to jump outside Visual Studio and use the command line application wsdl.exe.

wsdl /out:WSProxy.cs http://domain/MyWebService/Endpoint.asmx?WSDL

The stub this lovely little creates has a constructor that directly sets the URL.

public MyWebService()
{
    this.Url = "http://localhost/MyWebService/Endpoint.asmx";
}

Add an override constructor to provide your own URL to make this class more diverse.

public MyWebService(string url)
{
    this.Url = url;
}

Then, get really lazy like me and add a switch based on a command line arg.

if (arg1.Equals("LOCAL"))
    proxy = new WSProxy()

else if (arg1.Equals("QA"))
    proxy = new WSProxy("http://domain/MyWebService/Endpoint.asmx");

Post Navigation

Follow

Get every new post delivered to your Inbox.