ExposureRoom Home
  Log in Sign Up
Shiv's Website
Shiv Kumar
United States
Friends: 69
Focused on : 1
 
       
                                                             

Using Basic Authentication

Not Rated YetNot Rated YetNot Rated YetNot Rated YetNot Rated Yet0votes
January 10, 2008 12:05 PM  Views:124   Favorited:0 Comments:0
Filed Under:  Programming
Tags:  Delphi, ISAPI
 
Before we begin, let me say, that I expect, you've read the earlier article Authentication And Security in IIS. This article deals purely with the Basic Authentication Scheme. There is a demo you can play with before you begin. Please note a few important points before you try the demo. These points apply to the project as well.
  1. The username and password are shiv and matlus respectively
  2. If you go past 3 tries, please close you browser and start over. This is normal/expected due to reasons explained in the tutorial below.
  3. You can find the demo here

Getting the Browser to Pop Up the Dialog Box

First lets see how we can get the browser to pop up the log in dialog box. Once we know how to do this, it's a matter of retrieving/extracting the user name and password and verifying the user.

We need to do two things:

  1. Set the Status Code to 401
  2. Set the WWWAuthenticate HTTP header to Basic realm=Secured Site
This may sound complicated for the first timer, but is simply achieved in Delphi. The Response object has a property called StatusCode as well as WWWAuthenticate, so we can set these easily as shown below
    Response.StatusCode := 401;
    Response.WWWAuthenticate := AuthenticationString;
The moment we do this, the browser pops up a dialog box similar to that shown in Figure 1


Figure 1 showing the Log in Screen (I.E) for Basic Authentication

When the user clicks on the OK or Cancel buttons, the request returns to the same action that initially responded with the 401 status code and the WWWAuthenticate header. So we get a second chance at confirming the users validity in our action in the ISAPI.

In I.E, if the user name and password combination is incorrect I.E will automatically respond with the dialog once again. But it will do this only 3 times. Netscape on the other hand will continue to show this dialog until the user hits the cancel button or quits the session.

Getting Started

  1. Start with a new ISAPI Project
  2. Save the project as BasicAuth
  3. Create an Action item and set the Default property to True
In the event of this action item write the following code:
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
{ Entry point into the application }
  if UserIsAuthorized then
  begin
    Response.StatusCode := 200;
    Response.Content := 'You are Welcome Here';
  end
  else
  begin
    Response.StatusCode := 401;
    Response.WWWAuthenticate := AuthenticationString;
    Response.Content := '<H1>Access Denied</H1>';
  end;
end;
Before we go on, we need to understand a key point here. The browser treats the user name and password just like a cookie in that, it sends the user name and password with every request from the time the WWWAuthenticate header was set. This functionality makes it really simple to validate a user in any action of our ISAPI application. What we need to do is check the validity of the user in every action that needs the proper access rights. If the user is not valid, we simply send back an Access denied message or, set the status code to 401 and set the WWWAuthenticate header to give the user the option to log in to the application at any point. This is more a business decision than anything else. What we need to remember/understand is that we have the option to check the validity of a given user in any action that needs to be secured.

The function UserIsAuthorized does the real validation of the user name and password pair. If the user is authorized, we need to set the StatusCode property of the Response object to 200 to inform the browser of the confirmation. If the user is not authorized we need to set the StatusCode property to 401 and the WWWAuthenticate header to Basic realmxxx.

In the code above, the variable (actually a constant) AuthenticationString is declared as

const
  AuthenticationString = 'Basic realm=Secured Site';
Notice in Figure 1 that the Realm is the realm defined in the WWWAuthenticate header. The realm is nothing but a user defined string to identify the secured realm of the site. You can define this to be anything you wish. Again, in Figure 1 notice the Server field. This is the domain name of the server. If the user used the IP address to get to this site, then this dialog would show the server to be the IP address. In other words, you don't have control over this. This field simply indicates to the visitor, the site he is about to login to.

Extracting the User Name and Password

In the previous article, we learned that the browser sends the user name and password in clear text format. This is obviously not a secure method in and of itself, but if the transport layer is secure, such as SSL, then this method is safe.

In our ISAPI however we don't get this information in clear text format. IIS encodes this information in Base64 format. This makes extracting the user name and password a bit tricky. If we were to use an ISAPI filter application rather than an ISAPI extension application we would have access to this information in clear text format. Since we're building an ISAPI extension application, we need to decode this information in order to be able to read and validate it. Along with the project files you will find a unit called MsMIME. You need to add this unit to the uses clause of the interface section. This unit contains the functions DecodeUserNameAndPassword and MimeDecodeString used in the function UserIsAuthorized. The second function simply decodes the Base64 encoded data passed to us by the web server. The first function extracts the user name and password. But before we get into that, lets look at the UserIsAuthorized function.

function TWebModule1.UserIsAuthorized: Boolean;
var
  UserName : string;
  Password : string;
begin
  DecodeUserNameAndPassword(Request.Authorization, UserName, Password);
  { One can use any mode to assertain the validity of the user here }
  { It is reccomended however that if passwords are stored in a database
    or file that they be in an encrypted format such as MD5, SHA etc. }
  if (UserName = 'shiv') and (Password = 'matlus') then
    Result := True
  else
    Result := False;
end;
The Request object's Authorization property contains the user name and password encoded in Base64. Basically, the user name and password are in the format
 Basic c2hpdjptYXRsdXM=
the data after the word Basic is the user name and password in the form
  username:password
A colon as shown above separates the user name and password. But first we need to decode the string and then extract the user name and password from it. This is exactly what the function DecodeUserNameAndPassword does. This function looks like this:
procedure DecodeUserNameAndPassword(EncodedString : string;
  var UserName, Password : string);
var
  DecodedString : string;
begin
  { EnocodedString  will look like this:Basic c2hpdjptYXRsdXM= }
  { Remove the word Basic }
  EncodedString := Copy(EncodedString, 7, Length(EncodedString));
  { Use only the text upto the "=" sign }
  EncodedString := Copy(EncodedString,1, Pos('=',EncodedString) -1);
  { Decode the text }
  DecodedString := MimeDecodeString(EncodedString);
  { Extract the UserName and Password }
  UserName := Copy(DecodedString,1,Pos(':', DecodedString) -1);
  Delete(DecodedString,1,Pos(':', DecodedString));
  Password := DecodedString;
end;
This basically takes care of getting the browser to show the login dialog box and then extract the user name and password from the data sent to us in the Response object's Authorization property.

Handling other actions

For the sake of clarity, lets create one more action in our ISAPI project and see how we would handle authorized / unauthorized users to simulate a real world application. The OnAction event of the second action item would look like this:
procedure TWebModule1.WebModule1WebActionItem2Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
{ In all other actions, that have restricted access, you must check the
  validity of the user profile. The browser will send in the user name and
  password in each request. The user name and password will be the same for
  that given session (until the browser is closed }
  if UserIsAuthorized then
  begin
    Response.StatusCode := 200;
    Response.Content := 'Welcome';
  end
  else
  begin
    Response.StatusCode := 401;
    { Put the next line here if you want to allow users to login at any entry point
      in the application. This will pop up the login dialog }
    //Response.WWWAuthenticate := AuthenticationString;
    Response.Content := '<H1>Access Denied</H1>';
  end;
end;
Set the PathInfo property of this action item to access2.

So what would happen if a user attempted to go to this URL directly without being authenticated by the previous action? S/he will get the message Access Denied. Why? Because the browser did not send any user name and password information to our action for us to validate the user and so the function UserIsValid failed. Remember that the user name and password need to be treated like session cookies, in that the browser retains that information for the duration of its life/instance. If a user attempts to go directly to a section of a site that is secured without first logging in, the Request.Authorization property does not carry this information (assuming the user closed his browser and then attempted to go to a secured part of the site directly).

Depending on the business rules /requirements, we could pop up a login dialog if such were the case, giving the user an option to login at any point (action) in the application. This is explained in the code listing above.

Note: It was found that IIS 4&5 do not support Basic Authentication as expected (As per W3C specifications). That is, with Basic Authentication turned on, this project will not work as expected. To use Basic Authentication with IIS, you need only have "Allow Anonymous" checked.

The process that is followed in IIS for any authentication process is to first verify the user name and password as an NT user on the web server. If this user is valid, then the actual authentication process is executed. If the user name and password is not a valid NT account, then the authentication process fails without attempting to actually authenticate using the process desired.

Note for Omni users Thanks to Brian Gantzler, it was found out that Omni does not send the = at the end of the encrypted text. This makes the decode function - DecodeUserNameAndPassword fail. If you intend to use Omni, please make the required changes to this function to allow for this anomaly.

Comments have been Disabled for this post





Menus

Theme

Privacy Policy  |  Terms Of Service  |  Contact Us  |  Support  |  Help/FAQs  |  News