I have an ASP.NET MVC application that has a .asmx web service
I wrote an action filter attribute that I wanted to use on web methods on the web service, to check the Request headers for a UserID and Password, and throw an unauthorized response code if invalid or not present.
However, they dont appear to get called! Breakpoints just dont get hit.
Firstly, is using MVC attributes an acceptable way of authorizing web service called on an ASMX web service?
Secondly, is there a better/more efficient way of authorizing web service method calls?
In answer to your first question, MVC filters and Web API filters cannot be triggered by ASMX web services.
Action Filters are part of the MVC pipeline, triggered before (or after) an Action Method on a Controller (or API Controller) is executed. They can only be used within the MVC framework.
Action Filter override a virtual method on a MVC Controller (OnActionExecuting
). As only MVC Controllers have such methods, and only the MVC pipeline checks for them
To make matters worse, ASMX services, by default, use SOAP protocol rather than HTTP protocol. SOAP services are not able to access HTTP contexts (e.g. HttpContext.Current.User
) or HTTP Frameworks.
Web services can be configured to use the HTTP protocol. But, even then, MVC specific attributes are of no help to you.
Ways to Authenticate legacy ASMX services
Ideal way is to add a Service Reference to your MVC 4 project, calling your ASMX method like any class library method from an [Authorize]
secured Action Method or Web API method.
This way, you can leverage your MVC or Web API Authentication filters.
If you prefer to secure your ASMX service directly, you can check to HttpContext.Current.User
with Forms Authentication by configuring your ASMX service to use HTTP protocol.
in your web.config
<location path="SecuredMethod.asmx">
<system.web>
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
</webServices>
</system.web>
</location>
The MVC and Web API pipeline are not directly compatible with the older style of .ASMX web services. This is why those attributes do not fire when you place them on your web methods. Depending on your code base you could convert (rewrite) your code to the Web API 2 platform which is the new recommended way to write services. This has the following advantages over the traditional web services (.asmx).
xml
and json
. This can be significantly easier to work with for a client as the payload sent and received is usually much simpler not to mention light weight (the SOAP envelop used in .asmx services is very bloated.)The question then becomes "Should you convert your existing code?" That depends on:
What I would avoid is writing a wrapper in Web API that calls through to the Web Service.
Lets now assume that you want to continue with the .ASMX web services and address the question How can you execute Authorization on a web service
.
The traditional way to authenticate is to include the authentication information in the SOAP header. This can be easily accomplished using the existing SoapHeaderAttribute
.
SoapHeaderAttribute
on your web method(s)I prefer to create an abstract base service that my other services inherit from. This should create a little less duplicate code. Here is a full example without the authentication details. In this example I use a traditional user name and password but really it can be anything (token, a password hash, HMAC info, etc) and as that is a little out of scope for the question I will not get into authentication details.
using System.Web.Services;
using System.Web.Services.Protocols;
public abstract class AuthorizedWebService : System.Web.Services.WebService
{
// authentication info
public Authentication Authentication { get; set; }
// execute the actual authentication and authorization check
protected virtual void Authorize()
{
// check the Authentication instance object (passed in credentials)
// if not authenticate or authorized
// throw new System.Security.Authentication.AuthenticationException();
}
}
// authentication info
public class Authentication : SoapHeader
{
public string Username { get; set; }
public string Password { get; set; }
}
[WebService(Namespace = "http://tempuri.org/")]
public class MyTraditionalWebService : AuthorizedWebService
{
[WebMethod(Description = "Some web method.")]
[SoapHeader("Authentication")]
public string HelloWorld()
{
base.Authorize();
return "Hello " + base.Authentication.Username;
}
}
You can use Active Directory authentication. In c# a client would then pass in the credentials using the NetworkCredential class. Essentially what the client does is apply the authentication credentials to the HTTP header. I found this rather good SO answer on what the NetworkCredential class will actually translate into when making an HTTP call. You have to configure IIS so that authentication and authorization occur before the request reaches your method. You could also execute custom code for the authorization directly in your web methods (similar to above) but not for the authentication.
As for Forms Authentication there is no good way to do this with Web Services that has any advantage over the above specified Traditional / defacto
way. However, if you already have a web site setup which (now) includes your .asmx services then you could include authorization for the service in the web.config
assuming that the client has already authenticated to your site.
You could also write a custom HttpModule which handles the authentication and possible Authorization. The pro of this approach is you decouple the business logic in the web service from the authentication/authorization. This is a double edged sword though as you will have to maintain a module that parses the url and also the intended method to see if the request is authorized. This can lead to a fragile coupling.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With