Adam
Adam

Reputation: 344

WCF custom operation based authentication

I am trying to implement a token based authentication for internal/external users of a WCF service based on the method they are calling from the service. I have tried to intercept the incoming soap envelope and extract the operation call in the envelope with the token in the global.asax file with success and authenticate it, however when the call completes it does not pass through to the requested operation in the service. Is there a way to capture the incoming request and do some authenticating before it gets to the requested call? Here is my current code.

Global.asax

  protected void Application_AuthorizeRequest(object sender, EventArgs e)
    {
        bool authorized = false;
        bool soapRequestReceived = false;
        try
        {

            XmlDocument soapDocument = new XmlDocument();
            Stream receiveStream = HttpContext.Current.Request.InputStream;
            receiveStream.Position = 0;
            if (receiveStream.Length > 0)
            {
                soapRequestReceived = true;
                using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
                {
                    // Load into XML document
                    soapDocument.Load(readStream);

                    logger.Info("output of XML document received is " + readStream);
                }
                authorized = XMLSoapParser.ParseMethodNameAndToken(soapDocument);
                if (!authorized)
                {
                    throw new Exception("User not authorized!");
                }
            }
            return;

        }
        catch (Exception EX_NAME)
        {
            if (soapRequestReceived && !authorized)
            {
                throw new Exception("User not authorized!");
            }
            // eat it;
        }
    }

here is the code for the xml parser

    public static class XMLSoapParser
{
    public static bool ParseMethodNameAndToken(XmlDocument doc)
    {
        try
        {
            XmlElement rootElement = doc.DocumentElement;
            XmlNodeList nodes = rootElement.ChildNodes;

            string methodName = "";
            string token = "";
            for (int i = 0; i < nodes.Count; i++)
            {
                if (nodes.Item(i).Name.ToLower().Contains("body"))
                {
                    XmlNodeList bodyList = nodes.Item(i).ChildNodes;
                    for (int j = 0; j < bodyList.Count; j++)
                    {
                        if (bodyList.Item(j).Name.ToLower().Contains("tem:"))
                        {
                            methodName = bodyList.Item(j).Name.Substring(4);
                            Logger.Info("Method Name received! : " + methodName );
                            XmlNodeList methodNodeList = bodyList.Item(j).ChildNodes;
                            for (int k = 0; k < methodNodeList.Count; k++)
                            {
                                if (methodNodeList.Item(j).Name.ToLower().Contains("token"))
                                {
                                    token = methodNodeList.Item(j).InnerText;
                                    Logger.Info("Token received ! : " + token);
                                }
                            }
                        }
                    }
                }
            }
            var client = new AuthAdminClient("BasicHttpBinding_IAuthAdmin");
            var authorized = client.IsAuthorized(token, "Partners", methodName);
            return authorized;
        }
        catch (Exception exception)
        {
            Logger.Warn(
                string.Format(
                    "Execption encountered at XML Soap Parser with parameter {0} /r/n Exeception caught : {1} /r/n {2}",
                    doc.ToString(), exception.Message, exception.StackTrace));
            return false;
        }
    }
}

Upvotes: 0

Views: 179

Answers (1)

MvdD
MvdD

Reputation: 23436

Even though your solution may work, I would not advise to parse the SOAP envelope myself.

Normally, you would derive from ClaimsAuthorizationManager and override CheckAccess. The first parameter is of type AuthorizationContext, which has an Action property from which you can get the method called.

Returning false from CheckAccess will prevent the call from being dispatched to the method.

EDIT: To hook this up, you create a ServiceHost derived class and override the ApplyConfiguration method. In this method, you create configure the identity model something like this:

var credentials = this.Description.Behaviors.Find<ServiceCredentials>();
var identityConfiguration = new IdentityConfiguration();
// configure IdentityConfiguration properties here

credentials.IdentityConfiguration = identityConfiguration;
credentials.UseIdentityConfiguration = true;
identityConfiguration.ClaimsAuthorizationManager = new MyClaimsAuthorizationManager();

This is not the complete code, but should give you enough direction to find some complete examples. See here on how to activate the servicehost.

Upvotes: 2

Related Questions