Asmx web service basic authentication
asmx, basic-authentication, web-services
Solution
Change your custom HttpModule code to this:
public class BasicAuthHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new
EventHandler(this.OnAuthenticateRequest);
application.EndRequest += new
EventHandler(this.OnEndRequest);
}
public void OnAuthenticateRequest(object source, EventArgs
eventArgs)
{
HttpApplication app = (HttpApplication)source;
string authHeader = app.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader))
{
string authStr = app.Request.Headers["Authorization"];
if (authStr == null || authStr.Length == 0)
{
return;
}
authStr = authStr.Trim();
if (authStr.IndexOf("Basic", 0) != 0)
{
return;
}
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];
if (!MyUserValidator.Validate(username, password))
{
DenyAccess(app);
return;
}
}
else
{
app.Response.StatusCode = 401;
app.Response.End();
}
}
public void OnEndRequest(object source, EventArgs eventArgs)
{
if (HttpContext.Current.Response.StatusCode == 401)
{
HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401;
context.Response.AddHeader("WWW-Authenticate", "Basic Realm");
}
}
private void DenyAccess(HttpApplication app)
{
app.Response.StatusCode = 401;
app.Response.StatusDescription = "Access Denied";
app.Response.Write("401 Access Denied");
app.CompleteRequest();
}
}
Then enable `Anonymous authentication` and disable `Basic`, `Digest` and `Windows` authentication for your website in IIS.
Note: This implementation will work with WCF too.
Problem
I want to implement basic authentication using username and password validation in my asmx web service. I don't want to use WCF and I know this is not secure way, but I need to use basic authentication without using https. My web service is like this: ``` [WebService(Namespace = "http://www.mywebsite.com/")] public class Service1 { [WebMethod] public string HelloWorld() { return "Hello world"; } } ``` And I use this custom HttpModule: ``` public class BasicAuthHttpModule : IHttpModule { void IHttpModule.Init(HttpApplication context) { context.AuthenticateRequest += new EventHandler(OnAuthenticateRequest); } void OnAuthenticateRequest(object sender, EventArgs e) { string header = HttpContext.Current.Request.Headers["Authorization"]; if (header != null && header.StartsWith("Basic")) //if has header { string encodedUserPass = header.Substring(6).Trim(); //remove the "Basic" Encoding encoding = Encoding.GetEncoding("iso-8859-1"); string userPass = encoding.GetString(Convert.FromBase64String(encodedUserPass)); string[] credentials = userPass.Split(':'); string username = credentials[0]; string password = credentials[1]; if(!MyUserValidator.Validate(username, password)) { HttpContext.Current.Response.StatusCode = 401; HttpContext.Current.Response.End(); } } else { //send request header for the 1st round HttpContext context = HttpContext.Current; context.Response.StatusCode = 401; context.Response.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", string.Empty)); } } void IHttpModule.Dispose() { } } ``` And in the web.config I use this: ``` <?xml version="1.0"?> <configuration> <appSettings/> <connectionStrings/> <system.web> <customErrors mode="Off" /> <compilation debug="true" targetFramework="4.0"/> <authentication mode="None"/> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="BasicAuthHttpModule" type="AuthService.BasicAuthHttpModule, AuthService" /> </modules> </system.webServer> </configuration> ``` The calling code is: ``` static void Main(string[] args) { var proxy = new Service1.Service1() { Credentials = new NetworkCredential("user1", "p@ssw0rd"), PreAuthenticate = true }; try { var result = proxy.HelloWorld(); Console.WriteLine(result); } catch (Exception e) { Console.WriteLine(e.Message); } Console.ReadKey(); } ``` when I use this web service, the service asks for basic authentication but `header` variable in the `OnAuthenticateRequest` method always is null and `MyUserValidator.Validate()` never run. EDIT The fiddler results: ``` POST http://www.mywebsite.com/Service1.asmx HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.4927) VsDebuggerCausalityData: uIDPo+drc57U77xGu/ZaOdYvw6IAAAAA8AjKQNpkV06FEWDEs2Oja2C+h3kM7dlDvnFfE1VlIIIACQAA Content-Type: text/xml; charset=utf-8 SOAPAction: "http://www.mywebsite.com/HelloWorld" Host: www.mywebsite.com Content-Length: 291 Expect: 100-continue Connection: Keep-Alive <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorld xmlns="http://www.mywebsite.com/" /></soap:Body></soap:Envelope> HTTP/1.1 401 Unauthorized Cache-Control: private Content-Type: text/html Server: Microsoft-IIS/7.5 WWW-Authenticate: Basic realm="" X-AspNet-Version: 4.0.30319 WWW-Authenticate: Basic realm="www.mywebsite.com" X-Powered-By: ASP.NET Date: Sun, 03 Jun 2012 07:14:40 GMT Content-Length: 1293 ------------------------------------------------------------------ POST http://www.mywebsite.com/Service1.asmx HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.4927) VsDebuggerCausalityData: uIDPo+drc57U77xGu/ZaOdYvw6IAAAAA8AjKQNpkV06FEWDEs2Oja2C+h3kM7dlDvnFfE1VlIIIACQAA Content-Type: text/xml; charset=utf-8 SOAPAction: "http://www.mywebsite.com/HelloWorld" Authorization: Basic dXNlcjE6cEBzc3cwcmQ= Host: www.mywebsite.com Content-Length: 291 Expect: 100-continue <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorld xmlns="http://www.mywebsite.com/" /></soap:Body></soap:Envelope> HTTP/1.1 401 Unauthorized Content-Type: text/html Server: Microsoft-IIS/7.5 WWW-Authenticate: Basic realm="www.mywebsite.com" X-Powered-By: ASP.NET Date: Sun, 03 Jun 2012 07:14:41 GMT Content-Length: 1293 ------------------------------------------------------------------ ```