use Catalyst ();
use Digest::MD5 ();
-our $VERSION = "0.05";
+our $VERSION = "0.08";
sub authenticate_http {
my ( $c, @args ) = @_;
sub get_http_auth_store {
my ( $c, %opts ) = @_;
- $opts{store} || $c->config->{authentication}{http}{store};
+
+ my $store = $opts{store} || $c->config->{authentication}{http}{store} || return;
+
+ return ref $store
+ ? $store
+ : $c->get_auth_store($store);
}
sub authenticate_basic {
} split /,\s?/, substr( $authorization, 7 ); #7 == length "Digest "
my $opaque = $res{opaque};
- my $nonce = $c->_get_digest_authorization_nonce( __PACKAGE__ . '::opaque:' . $opaque );
+ my $nonce = $c->get_digest_authorization_nonce( __PACKAGE__ . '::opaque:' . $opaque );
next unless $nonce;
$c->log->debug('Checking authentication parameters.')
my $realm = $res{realm};
my $user;
- my $store = $opts{store}
- || $c->config->{authentication}{http}{store}
- || $c->default_auth_store;
- $user = $store->get_user($username) if $store;
+ unless ( $user = $opts{user} ) {
+ if ( my $store = $c->get_http_auth_store(%opts) || $c->default_auth_store ) {
+ $user = $store->get_user($username);
+ }
+ }
unless ($user) { # no user, no authentication
$c->log->debug('Unknown user: $user.') if $c->debug;
my ( $c, %opts ) = @_;
$c->res->status(401);
+ $c->res->content_type('text/plain');
+ $c->res->body($c->config->{authentication}{http}{authorization_required_message} ||
+ $opts{authorization_required_message} ||
+ 'Authorization required.');
# *DONT* short circuit
my $ok;
my ( $c, $opts ) = @_;
if ( my $domain = $opts->{domain} ) {
- Catalyst::Excpetion->throw("domain must be an array reference")
+ Catalyst::Exception->throw("domain must be an array reference")
unless ref($domain) && ref($domain) eq "ARRAY";
my @uris =
sub _build_basic_auth_header {
my ( $c, $opts ) = @_;
- return $c->_join_auth_header_parts( Basic => $c->_build_auth_header_common );
+ return $c->_join_auth_header_parts( Basic => $c->_build_auth_header_common( $opts ) );
}
sub _build_digest_auth_header {
my $key = __PACKAGE__ . '::opaque:' . $nonce->opaque;
- $c->_store_digest_authorization_nonce( $key, $nonce );
+ $c->store_digest_authorization_nonce( $key, $nonce );
return $c->_join_auth_header_parts( Digest =>
$c->_build_auth_header_common($opts),
my $nonce = $package->new;
- my $algorithm = $opts->{algorithm}
- || $c->config->{authentication}{http}{algorithm}
- || $nonce->algorithm;
-
- $nonce->algorithm( $algorithm );
+ if ( my $algorithm = $opts->{algorithm} || $c->config->{authentication}{http}{algorithm}) {
+ $nonce->algorithm( $algorithm );
+ }
return $nonce;
}
return "$type " . join(", ", @parts );
}
-sub _get_digest_authorization_nonce {
+sub get_digest_authorization_nonce {
my ( $c, $key ) = @_;
$c->_check_cache;
$c->cache->get( $key );
}
-sub _store_digest_authorization_nonce {
+sub store_digest_authorization_nonce {
my ( $c, $key, $nonce ) = @_;
$c->_check_cache;
=head1 NAME
Catalyst::Plugin::Authentication::Credential::HTTP - HTTP Basic and Digest authentication
-for Catlayst.
+for Catalyst.
=head1 SYNOPSIS
use Catalyst qw/
Authentication
- Authentication::Store::Moose
+ Authentication::Store::Minimal
Authentication::Credential::HTTP
/;
L<Catalyst::Plugin::Authentication>. Both basic and digest authentication
are currently supported.
+When authentication is required, this module sets a status of 401, and
+the body of the response to 'Authorization required.'. To override
+this and set your own content, check for the C<< $c->res->status ==
+401 >> in your C<end> action, and change the body accordingly.
+
+=head2 TERMS
+
+=over 4
+
+=item Nonce
+
+A nonce is a one-time value sent with each digest authentication
+request header. The value must always be unique, so per default the
+last value of the nonce is kept using L<Catalyst::Plugin::Cache>. To
+change this behaviour, override the
+C<store_digest_authorization_nonce> and
+C<get_digest_authorization_nonce> methods as shown below.
+
+=back
+
=head1 METHODS
=over 4
-=item authorization_required
+=item authorization_required %opts
Tries to C<authenticate_http>, and if that fails calls
C<authorization_required_response> and detaches the current action call stack.
-=item authenticate_http
+This method just passes the options through untouched.
+
+=item authenticate_http %opts
Looks inside C<< $c->request->headers >> and processes the digest and basic
(badly named) authorization header.
-=item authorization_required_response
+This will only try the methods set in the configuration. First digest, then basic.
+
+See the next two methods for what %opts can contain.
+
+=item authenticate_basic %opts
+
+=item authenticate_digest %opts
+
+Try to authenticate one of the methods without checking if the method is
+allowed in the configuration.
+
+%opts can contain C<store> (either an object or a name), C<user> (to disregard
+%the username from the header altogether, overriding it with a username or user
+%object).
+
+=item authorization_required_response %opts
Sets C<< $c->response >> to the correct status code, and adds the correct
header to demand authentication data from the user agent.
+Typically used by C<authorization_required>, but may be invoked manually.
+
+%opts can contain C<realm>, C<domain> and C<algorithm>, which are used to build
+%the digest header.
+
+=item store_digest_authorization_nonce $key, $nonce
+
+=item get_digest_authorization_nonce $key
+
+Set or get the C<$nonce> object used by the digest auth mode.
+
+You may override these methods. By default they will call C<get> and C<set> on
+C<< $c->cache >>.
+
=back
+=head1 CONFIGURATION
+
+All configuration is stored in C<< YourApp->config->{authentication}{http} >>.
+
+This should be a hash, and it can contain the following entries:
+
+=over 4
+
+=item store
+
+Either a name or an object -- the default store to use for HTTP authentication.
+
+=item type
+
+Can be either C<any> (the default), C<basic> or C<digest>.
+
+This controls C<authorization_required_response> and C<authenticate_http>, but
+not the "manual" methods.
+
+=item authorization_required_message
+
+Set this to a string to override the default body content "Authorization required."
+
+=back
+
+=head1 RESTRICTIONS
+
+When using digest authentication, this module will only work together
+with authentication stores whose User objects have a C<password>
+method that returns the plain-text password. It will not work together
+with L<Catalyst::Authentication::Store::Htpasswd>, or
+L<Catalyst::Plugin::Authentication::Store::DBIC> stores whose
+C<password> methods return a hashed or salted version of the password.
+
=head1 AUTHORS
Yuval Kogman, C<nothingmuch@woobling.org>
Sascha Kiefer C<esskar@cpan.org>
+=head1 SEE ALSO
+
+RFC 2617 (or its successors), L<Catalyst::Plugin::Cache>, L<Catalyst::Plugin::Authentication>
+
=head1 COPYRIGHT & LICENSE
Copyright (c) 2005-2006 the aforementioned authors. All rights