use strict;
use Plack::Util::Accessor qw( realm authenticator );
sub prepare_app {
my $self = shift;
my $auth = $self->authenticator or die 'authenticator is not set';
if (Scalar::Util::blessed($auth) && $auth->can('authenticate')) {
$self->authenticator(sub { $auth->authenticate(@_[0,1]) }); # because Authen::Simple barfs on 3 params
} elsif (ref $auth ne 'CODE') {
die 'authenticator should be a code reference or an object that responds to authenticate()';
}
}
sub call {
my($self, $env) = @_;
my $auth = $env->{HTTP_AUTHORIZATION}
or return $self->unauthorized;
if ($auth =~ /^Basic (.*)$/) {
my($user, $pass) = split /:/, (MIME::Base64::decode($1) || ":");
$pass = '' unless defined $pass;
if ($self->authenticator->($user, $pass, $env)) {
$env->{REMOTE_USER} = $user;
return $self->app->($env);
}
}
return $self->unauthorized;
}
sub unauthorized {
my $self = shift;
my $body = 'Authorization required';
return [
401,
[ 'Content-Type' => 'text/plain',
'Content-Length' => length $body,
'WWW-Authenticate' => 'Basic realm="' . ($self->realm || "restricted area") . '"' ],
[ $body ],
];
}
1;
__END__
=head1 NAME
Plack::Middleware::Auth::Basic - Simple basic authentication middleware
=head1 SYNOPSIS
use Plack::Builder;
my $app = sub { ... };
builder {
enable "Auth::Basic", authenticator => \&authen_cb;
$app;
};
sub authen_cb {
my($username, $password) = @_;
return $username eq 'admin' && $password eq 's3cr3t';
}
=head1 DESCRIPTION
Plack::Middleware::Auth::Basic is a basic authentication handler for Plack.
=head1 CONFIGURATION
=over 4
=item authenticator
A callback function that takes username and password supplied and
returns whether the authentication succeeds. Required.
Authenticator can also be an object that responds to C<authenticate>
method that takes username and password and returns boolean, so
backends for L<Authen::Simple> is perfect to use:
use Authen::Simple::LDAP;
enable "Auth::Basic", authenticator => Authen::Simple::LDAP->new(...);
=item realm
Realm name to display in the basic authentication dialog. Defaults to I<restricted area>.
=back
=head1 LIMITATIONS
This middleware expects that the application has a full access to the
headers sent by clients in PSGI environment. That is normally the case
with standalone Perl PSGI web servers such as L<Starman> or
L<HTTP::Server::Simple::PSGI>.
However, in a web server configuration where you can't achieve this
(i.e. using your application via Apache's mod_cgi), this middleware
does not work since your application can't know the value of
C<Authorization:> header.
If you use Apache as a web server and CGI to run your PSGI
application, you can either a) compile Apache with
C<-DSECURITY_HOLE_PASS_AUTHORIZATION> option, or b) use mod_rewrite to
pass the Authorization header to the application with the rewrite rule
like following.
RewriteEngine on
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
=head1 AUTHOR
Tatsuhiko Miyagawa
=head1 SEE ALSO
L<Plack>
=cut