There are quite a few samples on
the net which will provide alternate password protection schemes for
ASP.NET. There are also built in authentication and authorization
mechanisms which are great at what they do, are robust, proven, and
super flexible. Occasionally, however, you need something a little
simpler.
In my case, I generally need to provide a basic password protection for simple admin/reporting
tools or development environments I want to keep away from prying eyes. The optimal
solution for me would be something that I can simply set up in web.config and forget
about.
This
article will outline the basics of getting something as simple as that
set up and how you'd configure your web application once the code is
running.
Setting up browser-based authentication involves the following steps:
- Derive from the HttpModule base class
- Interpret the appropriate HTTP Headers
- Validate against a configuration or data store
- Modify the appropriate HTTP Headers based on the result
Code
In setting up our HttpModule, the event that we are most interested in is Application.AuthenticateRequest.
We need to setup a handler for it in the HttpModule.Init() method.
Once that's done, we can handle the bulk of the HTTP work that needs to be done.
This little bit of code sets up the variables and tests to see if we can even handle
this request.
HttpApplication app = (HttpApplication)source;
string authStr = app.Request.Headers["Authorization"];
/* If there's nothing in the header, then it's an anonymous request */
if (authStr == null || authStr.Length == 0) return;
/* If the authtype isn't Basic, then assume someone else will handle it. */
authStr = authStr.Trim();
if (authStr.IndexOf("Basic",0) != 0) return;
Next, we need to interpret the header information which is stored in Base64 for basic authentication.
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];
Finally, authenticate the user using the credentials we were presented with and
modify the headers to reflect the results. If we are successful, give the current
context a Principal based on our credentials in case that information is needed
further down the line.
if (AuthenticateUser(app, username, password, out roles))
{
string realm = "dieselrover";
app.Context.User = new GenericPrincipal(new GenericIdentity(username, realm),
roles);
}
else
{
DenyAccess(app);
}
The AuthenticateUser method simply checks the credentials against
some settings in the web.config. If you are interested in seeing that,
just download the project and browse through it. You can easily
override that method with your own if you want a more complicated data
store for usernames and passwords, but I would strongly suggest
evaluting a more robust solution if you need "real" authentication.
Configuration
Now lets look at how to set this up and get it running. For those of
you not interested in how it really works, you can just download the
binary, put it in your bin folder, and edit the web.config file.
First, set up the configuration handler:
<configSections>
<section name="MonoSite.Authentication"
type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
Second, configure the authentication settings by setting the username,
password, and realm. The realm is used by the client to organize saved
passwords.
<MonoSite.Authentication>
<add key="username" value="admin" />
<add key="password" value="admin1234" />
<add key="realm" value="dieselrover" />
</MonoSite.Authentication>
Third, setup the HttpModule.
<httpModules>
<add name="Authentication"
type="MonoSite.Http.AuthenticationModule,MonoSite.Http.Authentication" />
</httpModules>
Lastly, tell ASP.NET that you want there to be some sort of authentication.
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
If you want to only password protect certain subfolders of your application, you can do that easily by using the location tag.
<location path="admin">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
Source code and binaries