Introduction
Suppose that you are building a fancy new website and want to show your progress to your client. You create a new website in the Windows Azure management portal and deploy your code. You also want to make sure that only trusted users can access the website. Basic authentication seems like the most logical solution, but you suddenly realize that you cannot use basic authentication in Windows Azure websites in the same way you used it on your on-premises web server. In this article I will show you how to build your own HTTP managed module to achieve the same goal in Windows Azure websites.
Basic authentication was described in HTTP specification version 1.0 that was released way back in 1996. Basic authentication is a mechanism for a browser or other HTTP user agent to provide credentials when making a request to the server. This mechanism is supported by all major browsers and all major web servers. In the context of .NET web development, we have an IIS web server that provides basic authentication against Windows accounts on the server machine store or Active Directory.
The Windows Azure website is a relatively new feature for Windows Azure that was announced by Microsoft in June 2012. Now you can create a new website in Windows Azure and deploy your code in a matter of seconds. Windows Azure websites abstract you not only from the underlying hardware but from the software as well. You don’t need to worry about the disk space, memory, CPU, OS updates or even IIS management. All administration is done through the Windows Azure management portal.
As I mentioned earlier, IIS uses Windows accounts on the server machine for basic authentication. If you had access to a server running your website, you would create a new Windows account on that server, turn on basic authentication in IIS for your website, and use basic authentication. You cannot do that for your Windows Azure website, as you do not have access to the server running your website. Therefore, you cannot create new a Windows account. However, not all is lost.
The basic authentication protocol
Basic authentication uses a very simple protocol that you can easily implement and add to your website. On the server you need to send the HTTP 401 Not Authorized response code containing a WWW-Authenticate HTTP header when you want users to authenticate using basic authentication. The WWW-Authenticate header is a simple string with the realm used for authentication:
WWW-Authenticate: Basic realm="your realm"
The client side sends authentication credentials to the server using the Authorization header that is constructed like this:
"username:password" format is used to combine username and password into one string.
The resulting string is then encoded using Base64
The encoded string is send to the server with authentication method:
Authentication: Basic ‘base64 encoded username:password’
You can read the Wikipedia page on Basic authentication if you want more details, or you can take a deep dive into the specification itself.
Implementation
Now we have all of the information we need to create our own authentication using basic authentication protocol. The authentication mechanism can be divided into two parts:
Check whether the request is authenticated. If the request is not authenticated, send the HTTP 401 Not Authorized response code containing a WWW-Authenticate HTTP header.
When the Authorization header is received from the client, extract the username password pair and validate credentials.
Note: the implementation is based on the work by Mike Volodarsky.
When you enable basic authentication on your on-premises IIS, the HTTP 401 Not Authorized response code containing a WWW-Authenticate HTTP header is sent automatically by the web server. When implementing your own authentication mechanism, we have to make a decision for how to perform this check. One of the simplest ways is to check whether or not the client sent a cookie to the server. If the cookie is present in the request, we assume that the user went through the authentication process and we can let him through. If the cookie is not found we send the HTTP 401 Not Authorized response code containing a WWW-Authenticate HTTP header to the client. I am sure that there are more secure and reliable mechanisms that you can implement, but I wanted to keep it simple and efficient. The code itself is simple as well:
var context = ((HttpApplication)source).Context;
// if authentication cookie is not set issue a basic challenge
var authCookie = context.Request.Cookies.Get(AuthenticationCookieName);
if (authCookie == null)
{
//make sure that user is not authencated yet
if (!context.Response.Cookies.AllKeys.Contains(AuthenticationCookieName))
{
context.Response.Clear();
context.Response.StatusCode = HttpNotAuthorizedStatusCode;
context.Response.AddHeader(HttpWwwAuthenticateHeader, "Basic realm =\\"" + Realm +
"\\"");
}
}
So, we have implemented the first part – checking whether or not the request is authenticated. Now we will implement the actual authorization based on the information received from the client. I will let the code speak for itself:
var context = ((HttpApplication)source).Context;
string authorizationHeader = context.Request.Headers\[HttpAuthorizationHeader\];
// Extract the basic authentication credentials from the request
string userName = null;
string password = null;
if (!this.ExtractBasicCredentials(authorizationHeader, ref userName, ref password))
{
return;
}
// Validate the user credentials
if (!this.ValidateCredentials(userName, password))
{
return;
}
// check whether cookie is set and send it to client if needed
var authCookie = context.Request.Cookies.Get(AuthenticationCookieName);
if (authCookie == null)
{
authCookie = new HttpCookie(AuthenticationCookieName, "1") { Expires =
DateTime.Now.AddHours(1) };
context.Response.Cookies.Add(authCookie);
}
The logic is very simple:
Extract the credentials from the Authorization header.
Validate the credentials using the logic you need.
If the credentials are validated, send the cookie to the client to indicate that the authorization process is successful.
I have implemented a simple validation against a text file. Username and password pairs are read from the text file and then are used to validate the credentials received from the client.
All of the above code is useless if you do not know where to put it. We basically extend the IIS functionality, so the logical choice is to write a managed module. A managed module is a class that implements the System.Web.IHttpModule interface and is used to register for one or more events that occur in IIS request processing pipeline. We put our implemented logic into event handlers. The code that checks whether a request is authenticated should be called on EndRequest event. AuthenticateRequest event handler should have the code that authenticates the user based on credentials received from the client.
Now we have to enable a new module by adding it to the list of modules loaded by the application. You need to open web.config file and add the following line inside system.webServer/modules:
<add name="MyBasicAuthenticationModule"
type="Devbridge.BasicAuthentication.BasicAuthenticationModule"/>
It’s time to test it all. Build and launch your website. The browser should open the basic authentication login dialog for you. Success! We have implemented our very own basic authentication mechanism. The link to the full code can be found in Appendix A.
Final thoughts
As you can see, it is very easy to implement the basic authentication mechanism. However, you have to keep in mind that the credentials are passed in plain text and can be easily intercepted. Therefore, you have to think about using SSL encryption if you need more secure authentication.
Another thing to note is that HTTP specification does not provide a method for a server to log out users. Modern browsers will send the same credentials until the browser window is closed or user clears cached credentials.
I strongly advise you against using this module in a production environment. There are more secure and robust approaches that can be used in production.
If you decide to use this module in production use SSL encryption and generate strong passwords. Here are some tips to further extend this mechanism in order to make it more secure:
Stronger authentication cookie validation. Change the contents of the authentication cookie to include encrypted information that identifies the current session (for example, IP address). When the authentication cookie is received from the client, decrypt the contents and validate whether the cookie came from the same client.
Credential validation not using file. You can store your credentials in the database or even use Active Directory validation.
Appendix A
The full code that we are using in our staging environments can be accessed on our Azure Power Tools GitHub. The AzurePowerTools repository will contain Windows Azure related tools and extensions.
Basic authentication for Windows Azure websites module has relation to two projects:
Devbridge.BasicAuthentication project has the implementation for the basic authentication module.
Devbridge.BasicAuthentication.Test is a simple test website that can be used to test basic authentication.