REST based authentication: alternative solutions

Alternative solutions

This section points out some alternative solutions which might be useful under certain circumstances.

Setting REMOTE_USER

The REMOTE_USER variable is not set if a user has not been authenticated. However, this variable can be quite useful to have access to, especially in scripts. And that is where Jean-Michel's brilliant mod_perl hack comes into play. We can set REMOTE_USER to the user name present in the Authenticate header.

You will need to get and install mod_perl to make this work. Just follow the directions. Restart Apache when the module has been installed and yo have added the appropriate LoadModule statement to your httpd.conf.

In order to be able to put your Perl code in a subdirectory of your website root, it is useful to have a PerlRequire statement in your httpd.conf. For example my httpd.conf contains statement like this:

PerlRequire /var/www/perl/startup.pl

startup.pl's content is simply this:

use lib qw(/var/www/perl);
1;

This allows us to put Perl code in the /var/www/perl directory on our website so we don't have to install it in some Perl lib directory. Now back to our goal to make REMOTE_USER available, even when the user has not been authenticated. I differ here slightly from Jean-Michel who handled all the authentication himself and because he used Basic authentication he always had the proper user and password to use. I just assume that Authenticate is always correct and will use Apache's built-in authentication when we really need to authenticate sensitive data.

The first thing we need to do is to indicate to Apache that it always should run our "fix" code that will make REMOTE_USER available. We do that by adding a PerlFixupHandler to our VirtualHost or Directory directive in httpd.conf:

PerlFixupHandler MyHandler::Authenticate_Opt

Now we need to create /var/www/perl/MyHandler/Authenticate_Opt.pm (or wherever you want to have your Perl code, as long as Apache can find it). The code is this:

package MyHandler::Authenticate_Opt;
use strict;
use warnings;
use Apache2::Const qw/:common/;
use Apache2::RequestUtil;
use Apache2::RequestRec;
use APR::Table;
use MIME::Base64;
use strict;
use Carp;

sub get_login
{
     my $r = shift;
     my $authorization = $r->headers_in-> {'Authorization'} || return;
     $authorization =~ s/Digest.+username=\"([^\"]*)\".*/$1/;
     my $user = $authorization;
     return $user;
}

sub handler
{
     my $r = shift;
     Apache2::RequestUtil->request($r);
     return OK unless $r->user eq "";
     my $login = get_login($r)    || return OK;
     return OK unless ($login);
     _auth_ok ($login) and do {
         $r->user($login);
     };
     return OK;
}

sub _auth_ok
{
    return 1;
}

1;
__END__

This solution is a bit different from Jean-Michel's code. We're using Digest authentication, so we can't really check the user's credentials because we don't know his password. Because data for which a user really must be authenticated can be put behind a properly guarded section of your website, I don't find this of great concern of the only goal is to display a customized section of the website.

This code therefore only pickups the username from the Authenticate header and sets it. This code works for Apache 2.2 and mod_perl 2.0.

Passing the username to a script

Passing the username to a script is very useful. If you use mod_perl and want to set REMOTE_USER you're done. But you could also pass the user's name to a script behind the browser's back by appending a query string with the user's name:

RewriteCond %{HTTP:Authorization} username=\"([^\"]+)\"
RewriteRule ^index.html$  authenticated.cgi?user=%1 [L]

Or you could redirect a user to his or her own page:

RewriteRule ^index.html$  %1.html [L]

The possibilities are endless. Use the technique that fits the cirumstances.

Logout without a dialog box on Mozilla/FireFox

Nano Documet realised that the techniques presented on my HTTP authentication can be used to create a logout without displaying any dialog box whatsoever even with FireFox/Mizlla.

Read his cleverly devised friendly logout functionally with just clicking a link.

User initiated log off in Internet Explorer

Internet Explorer 6 SP1 or newer also allows you to log off using JavaScript, i.e. from the client, without going through a browser round-trip to force log off. This is possible with using the ClearAuthenticationCache command:

document.execCommand("ClearAuthenticationCache");

I showed this already in my base solution, but you can use this code also to log off a user without making a round trip to the server of course.

User initiated log off in Mozilla

Mozilla also has a possiblity to forget user credentials. You will need to call nsIHttpAuthManager.clearall(), but it seems this function requires use of the Java Plugin, version 1.5 or higher.

Solutions that do not work

After reading the set REMOTE_USER section, some of you might think that using Apache's SetEnvIf might work as well. That would avoid using mod_perl entirely!. Perhaps something like this:

SetEnvIf Authorization username=\"([^\"]+)\" REMOTE_USER=$1

Unfortunately this does not work. The REMOTE_USER set in this case seems to be distinct from REMOTE_USER as used by Apache. At least this REMOTE_USER is not available in your .htaccess for example.

Appendix: other solutions on the internet

If you start searching for the phrase cookieless authentication, one of the top hits is how to do this with .NET. However, if you try to access the site that claims explains how, the first thing it wants to do is set a cookie. Sigh.

The presented solution goes against all RESTafarians believe in.

Some PHP authentication documention page also lists many solutions by commentators. Not everyone of them was really tested it seems, and others have become obsolete or might only work for basic authentication.