Basic Authentication HttpModule
An HttpModule using PostAuthenticateRequest so that Basic Authentication works in a site that is otherwise Forms authenticated.
HttpModule
Static initialized with an IAuthenticationService which takes username/password and returns an IPrincipal.
using System;
using System.Diagnostics;
using System.Net;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
namespace WebApplication1
{
public class BasicAuthenticationHttpModule : IHttpModule
{
private static IAuthenticationService _authenticationService;
public static void SetBasicAuthenticationValidator(IAuthenticationService authenticationService)
{
_authenticationService = authenticationService;
}
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += ContextOnAuthenticateRequest;
}
private static void ContextOnAuthenticateRequest(object sender, EventArgs eventArgs)
{
if (_authenticationService == null ||
HttpContext.Current == null ||
HttpContext.Current.User == null ||
HttpContext.Current.User.Identity.IsAuthenticated)
return;
try
{
IPrincipal principal = null;
var headers = HttpContext.Current.Request.Headers;
var authValue = headers[HttpRequestHeader.Authorization.ToString()];
//if no Basic header, don't prompt for it
if (authValue == null ||
string.IsNullOrWhiteSpace(authValue) ||
!authValue.StartsWith("Basic"))
{
return;
}
//try to parse for it
var parsedCredentials = ParseAuthorizationHeader(authValue);
if (parsedCredentials != null)
{
principal = _authenticationService.ValidateCredentials(
parsedCredentials.Item1, parsedCredentials.Item2);
}
if (principal == null)
{
Unauthorized(HttpContext.Current);
return;
}
SetPrincipal(principal);
}
catch (SecurityException)
{
throw;
}
catch (Exception ex)
{
throw new SecurityException("Unexpected error. See inner exception for details.", ex);
}
}
private static void Unauthorized(HttpContext context)
{
Trace.TraceWarning("Unauthorized basic credentials");
context.Response.Headers.Add("WWW-Authenticate", "Basic");
context.Response.StatusCode = 401;
context.Response.StatusDescription = "Invalid credentials";
context.ApplicationInstance.CompleteRequest();
}
private static Tuple<string, string> ParseAuthorizationHeader(string authHeader)
{
try
{
string base64Credentials = authHeader.Substring(6);
var credentials = Encoding.ASCII.GetString(Convert.FromBase64String(base64Credentials))
.Split(new[] { ':' });
if (credentials.Length != 2 ||
string.IsNullOrEmpty(credentials[0]) ||
string.IsNullOrEmpty(credentials[1]))
{
return null;
}
return new Tuple<string, string>(credentials[0], credentials[1]);
}
catch (Exception exception)
{
Trace.TraceError("BasicAuthentication: ParseHeader " + exception);
throw;
}
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = Thread.CurrentPrincipal;
}
public void Dispose()
{
}
}
}
Registration
<system.webServer>
<modules>
<add name="BasicAuthenticationHttpModule" type="WebApplication1.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
Global.asax
void Application_Start(object sender, EventArgs e)
{
BasicAuthenticationHttpModule.SetBasicAuthenticationValidator(new AuthenticationService());
}
Async
If AuthenticationService is async, in .net 4.5 you can:
public void Init(HttpApplication context)
{
var asyncHelper = new EventHandlerTaskAsyncHelper(ContextOnAuthenticateRequest);
context.AddOnPostAuthenticateRequestAsync(asyncHelper.BeginEventHandler, asyncHelper.EndEventHandler);
}
private static async Task ContextOnAuthenticateRequest(object sender, EventArgs eventArgs)
{