3 package Catalyst::Plugin::Authentication::Credential::Password;
4 use base qw/Class::Accessor::Fast/;
10 use Catalyst::Exception ();
14 __PACKAGE__->mk_accessors(qw/_config/);
18 my ($class, $config, $app) = @_;
20 my $self = { _config => $config };
23 $self->_config->{'password_field'} ||= 'password';
24 $self->_config->{'password_type'} ||= 'clear';
25 $self->_config->{'password_hash_type'} ||= 'SHA-1';
27 my $passwordtype = $self->_config->{'password_type'};
28 if (!grep /$passwordtype/, ('clear', 'hashed', 'salted_hash', 'crypted', 'self_check')) {
29 Catalyst::Exception->throw(__PACKAGE__ . " used with unsupported password type: " . $self->_config->{'password_type'});
35 my ( $self, $c, $authstore, $authinfo ) = @_;
37 ## because passwords may be in a hashed format, we have to make sure that we remove the
38 ## password_field before we pass it to the user routine, as some auth modules use
39 ## all data passed to them to find a matching user...
40 my $userfindauthinfo = {%{$authinfo}};
41 delete($userfindauthinfo->{$self->_config->{'password_field'}});
43 my $user_obj = $authstore->find_user($userfindauthinfo, $c);
45 if ($self->check_password($user_obj, $authinfo)) {
49 $c->log->debug("Unable to locate user matching user info provided");
55 my ( $self, $user, $authinfo ) = @_;
57 if ($self->_config->{'password_type'} eq 'self_check') {
58 return $user->check_password($authinfo->{$self->_config->{'password_field'}});
60 my $password = $authinfo->{$self->_config->{'password_field'}};
61 my $storedpassword = $user->get($self->_config->{'password_field'});
63 if ($self->_config->{'password_type'} eq 'none') {
65 } elsif ($self->_config->{'password_type'} eq 'clear') {
66 return $password eq $storedpassword;
67 } elsif ($self->_config->{'password_type'} eq 'crypted') {
68 return $storedpassword eq crypt( $password, $storedpassword );
69 } elsif ($self->_config->{'password_type'} eq 'salted_hash') {
70 require Crypt::SaltedHash;
71 my $salt_len = $self->_config->{'password_salt_len'} ? $self->_config->{'password_salt_len'} : 0;
72 return Crypt::SaltedHash->validate( $storedpassword, $password,
74 } elsif ($self->_config->{'password_type'} eq 'hashed') {
76 my $d = Digest->new( $self->_config->{'password_hash_type'} );
77 $d->add( $self->_config->{'password_pre_salt'} || '' );
79 $d->add( $self->_config->{'password_post_salt'} || '' );
81 my $computed = $d->clone()->digest;
82 my $b64computed = $d->clone()->b64digest;
83 return ( ( $computed eq $storedpassword )
84 || ( unpack( "H*", $computed ) eq $storedpassword )
85 || ( $b64computed eq $storedpassword)
86 || ( $b64computed.'=' eq $storedpassword) );
91 ## BACKWARDS COMPATIBILITY - all subs below here are deprecated
92 ## They are here for compatibility with older modules that use / inherit from C::P::A::Password
93 ## login()'s existance relies rather heavily on the fact that only Credential::Password
94 ## is being used as a credential. This may not be the case. This is only here
95 ## for backward compatibility. It will go away in a future version
96 ## login should not be used in new applications.
99 my ( $c, $user, $password, @rest ) = @_;
104 $user = $c->request->param("login")
105 || $c->request->param("user")
106 || $c->request->param("username")
109 "Can't login a user without a user object or user ID param")
117 $password = $c->request->param("password")
118 || $c->request->param("passwd")
119 || $c->request->param("pass")
121 $c->log->debug("Can't login a user without a password")
126 unless ( Scalar::Util::blessed($user)
127 and $user->isa("Catalyst::Plugin::Authentication::User") )
129 if ( my $user_obj = $c->get_user( $user, $password, @rest ) ) {
133 $c->log->debug("User '$user' doesn't exist in the default store")
139 if ( $c->_check_password( $user, $password ) ) {
140 $c->set_authenticated($user);
141 $c->log->debug("Successfully authenticated user '$user'.")
147 "Failed to authenticate user '$user'. Reason: 'Incorrect password'")
154 ## also deprecated. Here for compatibility with older credentials which do not inherit from C::P::A::Password
155 sub _check_password {
156 my ( $c, $user, $password ) = @_;
158 if ( $user->supports(qw/password clear/) ) {
159 return $user->password eq $password;
161 elsif ( $user->supports(qw/password crypted/) ) {
162 my $crypted = $user->crypted_password;
163 return $crypted eq crypt( $password, $crypted );
165 elsif ( $user->supports(qw/password hashed/) ) {
167 my $d = Digest->new( $user->hash_algorithm );
168 $d->add( $user->password_pre_salt || '' );
170 $d->add( $user->password_post_salt || '' );
172 my $stored = $user->hashed_password;
173 my $computed = $d->clone()->digest;
174 my $b64computed = $d->clone()->b64digest;
176 return ( ( $computed eq $stored )
177 || ( unpack( "H*", $computed ) eq $stored )
178 || ( $b64computed eq $stored)
179 || ( $b64computed.'=' eq $stored) );
181 elsif ( $user->supports(qw/password salted_hash/) ) {
182 require Crypt::SaltedHash;
185 $user->can("password_salt_len") ? $user->password_salt_len : 0;
187 return Crypt::SaltedHash->validate( $user->hashed_password, $password,
190 elsif ( $user->supports(qw/password self_check/) ) {
192 # while somewhat silly, this is to prevent code duplication
193 return $user->check_password($password);
197 Catalyst::Exception->throw(
198 "The user object $user does not support any "
199 . "known password authentication mechanism." );
211 Catalyst::Plugin::Authentication::Credential::Password - Authenticate a user
220 package MyApp::Controller::Auth;
223 my ( $self, $c ) = @_;
225 $c->authenticate( { username => $c->req->param('username'),
226 password => $c->req->param('password') });
231 This authentication credential checker takes authentication information
232 (most often a username) and a password, and attempts to validate the password
233 provided against the user retrieved from the store.
238 __PACKAGE__->config->{authentication} =
240 default_realm => 'members',
246 password_field => 'password',
247 password_type => 'hashed',
248 password_hash_type => 'SHA-1'
253 The password module is capable of working with several different password
254 encryption/hashing algorithms. The one the module uses is determined by the
255 credential configuration.
257 Those who have used L<Catalyst::Plugin::Authentication> prior to the 0.10 release
258 should note that the password field and type information is no longer part
259 of the store configuration and is now part of the Password credential configuration.
265 The classname used for Credential. This is part of
266 L<Catalyst::Plugin::Authentication> and is the method by which
267 Catalyst::Plugin::Authentication::Credential::Password is loaded as the
268 credential validator. For this module to be used, this must be set to
273 The field in the user object that contains the password. This will vary
274 depending on the storage class used, but is most likely something like
275 'password'. In fact, this is so common that if this is left out of the config,
276 it defaults to 'password'. This field is obtained from the user object using
277 the get() method. Essentially: $user->get('passwordfieldname');
281 This sets the password type. Often passwords are stored in crypted or hashed
282 formats. In order for the password module to verify the plaintext password
283 passed in, it must be told what format the password will be in when it is retreived
284 from the user object. The supported options are:
290 No password check is done. An attempt is made to retrieve the user based on
291 the information provided in the $c->authenticate() call. If a user is found,
292 authentication is considered to be successful.
296 The password in user is in clear text and will be compared directly.
300 This option indicates that the password should be passed to the check_password()
301 routine on the user object returned from the store.
305 The password in user is in UNIX crypt hashed format.
309 The password in user is in salted hash format, and will be validated
310 using L<Crypt::SaltedHash>. If this password type is selected, you should
311 also provide the B<password_salt_len> config element to define the salt length.
315 If the user object supports hashed passwords, they will be used in conjunction
316 with L<Digest>. The following config elements affect the hashed configuration:
320 =item password_hash_type
322 The hash type used, passed directly to L<Digest/new>.
324 =item password_pre_salt
326 Any pre-salt data to be passed to L<Digest/add> before processing the password.
328 =item password_post_salt
330 Any post-salt data to be passed to L<Digest/add> after processing the password.
340 The Password credential module is very simple to use. Once configured as
341 indicated above, authenticating using this module is simply a matter of
342 calling $c->authenticate() with an authinfo hashref that includes the
343 B<password> element. The password element should contain the password supplied
344 by the user to be authenticated, in clear text. The other information supplied
345 in the auth hash is ignored by the Password module, and simply passed to the
346 auth store to be used to retrieve the user. An example call follows:
348 if ($c->authenticate({ username => $username,
349 password => $password} )) {
350 # authentication successful
352 # authentication failed
357 There are no publicly exported routines in the Password module (or indeed in
358 most credential modules.) However, below is a description of the routines
359 required by L<Catalyst::Plugin::Authentication> for all credential modules.
363 =item new ( $config, $app )
365 Instantiate a new Password object using the configuration hash provided in
366 $config. A reference to the application is provided as the second argument.
367 Note to credential module authors: new() is called during the application's
368 plugin setup phase, which is before the application specific controllers are
369 loaded. The practical upshot of this is that things like $c->model(...) will
370 not function as expected.
372 =item authenticate ( $authinfo, $c )
374 Try to log a user in, receives a hashref containing authentication information
375 as the first argument, and the current context as the second.