Hi there.
I've been trying to write a system to perform authentication using the
www-authenticate (http authenatication) method. However, I need a client visiting the
page and having been authenticated to be able to logout and have their browser forget
the information (or at least be able to force the browser to re-authenticate within
the same browser session).
As most browsers cache login details (sensibly - you don't want to enter your login
details for each page you visit) the best system I can think of is to use dynamic
realms. This would mean that when someone first visits the site, they will be given a
random/sequential realm. When they then click on a logout button, the old realm is
discarded, and if the browser tries to authenticate with the same realm it is refused
(or alternatively the server just starts sending a different realm and the browser
re-authenticates on the new realm). Significantly, the system needs to be accessible
by several people simultaneously. The purpose is actually to allow someone accessing
the site to log in, be served dynamic content on the basis of username (which I'm
already doing), but have the option to log out and then back in as a different user.
I've been looking at different ways to do this, but haven't had any sucess as of yet.
My first attempt was to use the module mod_auth_external to deal with authentication
using a perl script (which would allow me to arbitrarily refuse login details even if
they are correct, on the basis of other details such as realm). This is my preferable
method (mainly because I understand it), but it seems that there's no way to integrate
the realm into the auth_external system. This means that although I could refuse a
username and password, I can't change the realm dynamically, and so I can't force the
browser to re-authenticate (and without the realm information being passed to the
script, I don't know if a visitor has re-authenticated with the same username or if
their browser has simply sent the cached copy of the details).
After searching around for methods of dynamically assigning an authname, I stumbled
across mod_perl. I've never in my life even looked at mod_perl (and I don't really
understand object-oriented perl, I'm sorry to say - I came from a BASIC background -
in more ways than one apparently). I also found an example script for assigning
dynamic realms, but haven't been able to get it to work.
I have only been trying for a day, and I know it's bad of me to resort to a mailing
list after such a short space of time, but I've trawled the Web for as much
information as I can find but with no success.
The example mod_perl script I found was as follows:
<From http://www.davin.ottawa.on.ca/archive/modperl/2000-09/msg00839.phtml>
#############################################################################
Here's a simple handler that will set the AuthType and AuthName
dynamically and handle the authentication for you. This handler will
prompt you for a password when you try to acess /manual with the
AuthName, "The Manual" and prompt with the AuthName "The Icons" when you
try to access /icons. These urls are part of Apaches basic installation
(that's if you did not remove the manual from your htdocs directory).
The authentication phase will let you in just as long you supply a
username and password. You can of course code such that it you can
authenicate against a .htpassword file, using Apache::Htpasswd.
Anyhow, this should show you that you can indeed change the AuthName
on-the-fly and also handle
authentication without having to include AuthName,AuthType,AuthUserFile
explicitly in your httpd.conf.
Note: the authentication subroutine acted flaky, sometimes it worked and
other times it didn't. But the realms did change for the each uri.
i hope this helps you....have fun ;)
Setting it up:
In your httpd.conf ( in a global area):
PerlHeaderParserHandler Apache::SetRealm;
=code
package Apache::SetRealm;
use Apache;
use Apache::Constants qw(:common);
sub handler {
my $r = shift;
## Make Apache aware the we want to also handle the Authentication
phase using a custom
## handler, in this case the subroutine authenticate()
$r->push_handlers(PerlAuthenHandler => \&authenticate);
my $uri = $r->uri;
## only handle uri that are defined as protected, in this case the
only protected
## uri's are /icons and /manuals
return OK unless is_protected($r);
my $realm = get_realm($r);
## Construct the Header Field containing the type of authenticate
(Basic) and our
## realmname return by get_realm()
my $authheader = 'Basic realm="'.$realm.'"';
$r->header_out("WWW-Authenticate" ,$authheader);
## Return 401 to browser and prompt for login
$r->status(AUTH_REQUIRED);
$r->send_http_header("text/html");
return AUTH_REQUIRED;
}
sub get_realm {
## Get the AuthName for a specific uri. You can probably read these
off of a file that
## contains a list of uri's and realmNames
my $r = shift;
return "The Icons" if ($r->uri =~ /\/icons/);
return "The Manual" if ($r->uri =~ /\/manual/);
}
sub is_protected {
## Check the $uri requested matches our set of "Restricted"
locations
## 1 = isProtected, 0 = NotProtected
## You can probably have these protected areas in a seperate file,
the eagle book
## has some excellent ideas on how to acomplish this
my $r = shift;
my @protected = ('\/manual','\/icons');
for (@protected) { return 1 if ($r->uri =~ /$_/); }
return 0;
}
sub authenticate {
## Straight out of the Eagle Book
my $r = shift;
return OK if $r->sub_request;
my ($res,$password) = $r->get_basic_auth_pw;
return $res if $res != OK;
my $username = $r->connection->user;
unless ($username && $pass) {
$r->note_basic_auth_failure;
$r->log_reason("Did not provide username");
return AUTH_REQUIRED;
}
## Now that you have the $username and $password you can
## include your code to open your AuthUserFile to check the password
and username
## I suggest using Apache::Htpasswd, it provides all the
methods/functions that you need to
## accomplish this part of the task...
$r->log_reason("WELCOME $user");
return OK;
}
#############################################################################
I've made some minor changes (but the changes all seem to work). The specific error
I'm getting now is relating to the line:
return OK if $r->sub_request;
... in the authenticate subroutine. The error which appears in the apache error logs
is:
[Tue Dec 19 17:32:04 2000] [error] Can't locate object method "sub_request" via
package "Apache" at /usr/lib/perl5/5.6.0/i386-linux/raxware/AssignRealm.pm line 85.
As I say, I'm new to mod_perl and object-oriented perl, so maybe this is simple to
solve, but I can't find any online reference to the same error, or any reference to
the purpose of the method 'sub_request'.
Basically I was hoping someone could point me in the right direction for what to try
next. If there is something available already which does what I want (I want to avoid
cookie tracking due to the problems of cookie support, and URI tracking for security
reasons - also, as HTTP has an authentication system, I may as well try to use it)
that would be great. Alternatively, if someone could suggest a good online tutorial or
manual for the mod_perl system or suggest a possible solution to the above error, that
would also be great. Lastly, if anyone has tried anything similar (or hasn't) and has
any pointers or suggestions for alternative methods, they would be much appreciated.
Any advice would be gratefully received.
Cheers,
Andy.
--
Beauty is in the eye of the beholder... Oh, no - it's just an eyelash.