3 package Catalyst::Plugin::Authentication;
5 use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
8 __PACKAGE__->mk_accessors(qw/_user/);
9 __PACKAGE__->mk_classdata($_) for qw/_auth_realms/;
18 # this optimization breaks under Template::Toolkit
19 # use user_exists instead
22 # constant->import(have_want => eval { require Want });
25 our $VERSION = "0.10";
27 sub set_authenticated {
28 my ( $c, $user, $realmname ) = @_;
31 $c->request->{user} = $user; # compatibility kludge
34 $realmname = 'default';
37 if ( $c->isa("Catalyst::Plugin::Session")
38 and $c->config->{authentication}{use_session}
39 and $user->supports("session") )
41 $c->save_user_in_session($user, $realmname);
43 $user->_set_auth_realm($realmname);
45 $c->NEXT::set_authenticated($user, $realmname);
48 sub _should_save_user_in_session {
49 my ( $c, $user ) = @_;
51 $c->_auth_sessions_supported
52 and $c->config->{authentication}{use_session}
53 and $user->supports("session");
56 sub _should_load_user_from_session {
57 my ( $c, $user ) = @_;
59 $c->_auth_sessions_supported
60 and $c->config->{authentication}{use_session}
61 and $c->session_is_valid;
64 sub _auth_sessions_supported {
66 $c->isa("Catalyst::Plugin::Session");
76 if ( defined(my $user = $c->_user) ) {
79 return $c->auth_restore_user;
83 # change this to allow specification of a realm - to verify the user is part of that realm
84 # in addition to verifying that they exist.
87 return defined($c->_user) || defined($c->_user_in_session);
91 sub save_user_in_session {
92 my ( $c, $user, $realmname ) = @_;
94 $c->session->{__user_realm} = $realmname;
96 # we want to ask the backend for a user prepared for the session.
97 # but older modules split this functionality between the user and the
98 # backend. We try the store first. If not, we use the old method.
99 my $realm = $c->get_auth_realm($realmname);
100 if ($realm->{'store'}->can('for_session')) {
101 $c->session->{__user} = $realm->{'store'}->for_session($c, $user);
103 $c->session->{__user} = $user->for_session;
113 $c->isa("Catalyst::Plugin::Session")
114 and $c->config->{authentication}{use_session}
115 and $c->session_is_valid
117 delete @{ $c->session }{qw/__user __user_realm/};
120 $c->NEXT::logout(@_);
124 my ( $c, $userinfo, $realmname ) = @_;
126 $realmname ||= 'default';
127 my $realm = $c->get_auth_realm($realmname);
128 if ( $realm->{'store'} ) {
129 return $realm->{'store'}->find_user($userinfo, $c);
131 $c->log->debug('find_user: unable to locate a store matching the requested realm');
136 sub _user_in_session {
139 return unless $c->_should_load_user_from_session;
141 return $c->session->{__user};
144 sub _store_in_session {
147 # we don't need verification, it's only called if _user_in_session returned something useful
149 return $c->session->{__user_store};
152 sub auth_restore_user {
153 my ( $c, $frozen_user, $realmname ) = @_;
155 $frozen_user ||= $c->_user_in_session;
156 return unless defined($frozen_user);
158 $realmname ||= $c->session->{__user_realm};
159 return unless $realmname; # FIXME die unless? This is an internal inconsistency
161 my $realm = $c->get_auth_realm($realmname);
162 $c->_user( my $user = $realm->{'store'}->from_session( $c, $frozen_user ) );
164 # this sets the realm the user originated in.
165 $user->_set_auth_realm($realmname);
170 # we can't actually do our setup in setup because the model has not yet been loaded.
171 # So we have to trigger off of setup_finished. :-(
175 $c->_authentication_initialize();
179 ## the actual initialization routine. whee.
180 sub _authentication_initialize {
183 if ($c->_auth_realms) { return };
185 my $cfg = $c->config->{'authentication'} || {};
193 $c->_auth_realms($realmhash);
195 ## BACKWARDS COMPATIBILITY - if realm is not defined - then we are probably dealing
196 ## with an old-school config. The only caveat here is that we must add a classname
197 if (exists($cfg->{'realms'})) {
199 foreach my $realm (keys %{$cfg->{'realms'}}) {
200 $c->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
203 # if we have a 'default-realm' in the config hash and we don't already
204 # have a realm called 'default', we point default at the realm specified
205 if (exists($cfg->{'default_realm'}) && !$c->get_auth_realm('default')) {
206 $c->_set_default_auth_realm($cfg->{'default_realm'});
209 foreach my $storename (keys %{$cfg->{'stores'}}) {
211 store => $cfg->{'stores'}{$storename},
213 $c->setup_auth_realm($storename, $realmcfg);
221 sub setup_auth_realm {
222 my ($app, $realmname, $config) = @_;
224 $app->log->debug("Setting up $realmname");
225 if (!exists($config->{'store'}{'class'})) {
226 Carp::croak "Couldn't setup the authentication realm named '$realmname', no class defined";
230 my $storeclass = $config->{'store'}{'class'};
232 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
233 ## taken to mean C::P::A::Store::(specifiedclass)::Backend
234 if ($storeclass !~ /^\+(.*)$/ ) {
235 $storeclass = "Catalyst::Plugin::Authentication::Store::${storeclass}::Backend";
241 # a little niceness - since most systems seem to use the password credential class,
242 # if no credential class is specified we use password.
243 $config->{credential}{class} ||= "Catalyst::Plugin::Authentication::Credential::Password";
245 my $credentialclass = $config->{'credential'}{'class'};
247 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
248 ## taken to mean C::P::A::Credential::(specifiedclass)
249 if ($credentialclass !~ /^\+(.*)$/ ) {
250 $credentialclass = "Catalyst::Plugin::Authentication::Credential::${credentialclass}";
252 $credentialclass = $1;
255 # if we made it here - we have what we need to load the classes;
256 Catalyst::Utils::ensure_class_loaded( $credentialclass );
257 Catalyst::Utils::ensure_class_loaded( $storeclass );
259 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
260 # of get_user and add it to the class. this is because the auth routines use find_user,
261 # and rely on it being present. (this avoids per-call checks)
262 if (!$storeclass->can('find_user')) {
264 *{"${storeclass}::find_user"} = sub {
265 my ($self, $info) = @_;
266 my @rest = @{$info->{rest}} if exists($info->{rest});
267 $self->get_user($info->{id}, @rest);
271 $app->auth_realms->{$realmname}{'store'} = $storeclass->new($config->{'store'}, $app);
272 if ($credentialclass->can('new')) {
273 $app->auth_realms->{$realmname}{'credential'} = $credentialclass->new($config->{'credential'}, $app);
275 # if the credential class is not actually a class - has no 'new' operator, we wrap it,
276 # once again - to allow our code to be simple at runtime and allow non-OO packages to function.
277 my $wrapperclass = 'Catalyst::Plugin::Authentication::Credential::Wrapper';
278 Catalyst::Utils::ensure_class_loaded( $wrapperclass );
279 $app->auth_realms->{$realmname}{'credential'} = $wrapperclass->new($config->{'credential'}, $app);
285 return($self->_auth_realms);
289 my ($app, $realmname) = @_;
290 return $app->auth_realms->{$realmname};
294 # Very internal method. Vital Valuable Urgent, Do not touch on pain of death.
295 # Using this method just assigns the default realm to be the value associated
296 # with the realmname provided. It WILL overwrite any real realm called 'default'
297 # so can be very confusing if used improperly. It's used properly already.
298 # Translation: don't use it.
299 sub _set_default_auth_realm {
300 my ($app, $realmname) = @_;
302 if (exists($app->auth_realms->{$realmname})) {
303 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
305 return $app->get_auth_realm('default');
309 my ($app, $userinfo, $realmname) = @_;
312 $realmname = 'default';
315 my $realm = $app->get_auth_realm($realmname);
317 if ($realm && exists($realm->{'credential'})) {
318 my $user = $realm->{'credential'}->authenticate($app, $realm->{store}, $userinfo);
320 $app->set_authenticated($user, $realmname);
324 $app->log->debug("The realm requested, '$realmname' does not exist," .
325 " or there is no credential associated with it.")
330 ## BACKWARDS COMPATIBILITY -- Warning: Here be monsters!
332 # What follows are backwards compatibility routines - for use with Stores and Credentials
333 # that have not been updated to work with C::P::Authentication v0.10.
334 # These are here so as to not break people's existing installations, but will go away
335 # in a future version.
337 # The old style of configuration only supports a single store, as each store module
338 # sets itself as the default store upon being loaded. This is the only supported
339 # 'compatibility' mode.
343 my ( $c, $uid, @rest ) = @_;
345 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
349 ## this should only be called when using old-style authentication plugins. IF this gets
350 ## called in a new-style config - it will OVERWRITE the store of your default realm. Don't do it.
351 ## also - this is a partial setup - because no credential is instantiated... in other words it ONLY
352 ## works with old-style auth plugins and C::P::Authentication in compatibility mode. Trying to combine
353 ## this with a realm-type config will probably crash your app.
354 sub default_auth_store {
357 if ( my $new = shift ) {
358 $self->auth_realms->{'default'}{'store'} = $new;
359 my $storeclass = ref($new);
361 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
362 # of get_user and add it to the class. this is because the auth routines use find_user,
363 # and rely on it being present. (this avoids per-call checks)
364 if (!$storeclass->can('find_user')) {
366 *{"${storeclass}::find_user"} = sub {
367 my ($self, $info) = @_;
368 my @rest = @{$info->{rest}} if exists($info->{rest});
369 $self->get_user($info->{id}, @rest);
374 return $self->get_auth_realm('default')->{'store'};
377 ## BACKWARDS COMPATIBILITY
378 ## this only ever returns a hash containing 'default' - as that is the only
379 ## supported mode of calling this.
380 sub auth_store_names {
383 my %hash = ( $self->get_auth_realm('default')->{'store'} => 'default' );
387 my ( $self, $name ) = @_;
389 if ($name ne 'default') {
390 Carp::croak "get_auth_store called on non-default realm '$name'. Only default supported in compatibility mode";
392 $self->default_auth_store();
396 sub get_auth_store_name {
397 my ( $self, $store ) = @_;
401 # sub auth_stores is only used internally - here for completeness
405 my %hash = ( 'default' => $self->get_auth_realm('default')->{'store'});
416 Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
417 authentication framework.
426 $c->authenticate({ username => 'myusername', password => 'mypassword' });
427 my $age = $c->user->get('age');
432 The authentication plugin provides generic user support for Catalyst apps. It
433 is the basis for both authentication (checking the user is who they claim to
434 be), and authorization (allowing the user to do what the system authorises
437 Using authentication is split into two parts. A Store is used to actually
438 store the user information, and can store any amount of data related to the
439 user. Credentials are used to verify users, using information from the store,
440 given data from the frontend. A Credential and a Store are paired to form a
441 'Realm'. A Catalyst application using the authentication framework must have
442 at least one realm, and may have several.
444 To implement authentication in a Catalyst application you need to add this
445 module, and specify at least one realm in the configuration.
447 Authentication data can also be stored in a session, if the application
448 is using the L<Catalyst::Plugin::Session> module.
450 B<NOTE> in version 0.10 of this module, the interface to this module changed.
451 Please see L</COMPATIBILITY ROUTINES> for more information.
455 =head2 The Authentication/Authorization Process
457 Web applications typically need to identify a user - to tell the user apart
458 from other users. This is usually done in order to display private information
459 that is only that user's business, or to limit access to the application so
460 that only certain entities can access certain parts.
462 This process is split up into several steps. First you ask the user to identify
463 themselves. At this point you can't be sure that the user is really who they
466 Then the user tells you who they are, and backs this claim with some piece of
467 information that only the real user could give you. For example, a password is
468 a secret that is known to both the user and you. When the user tells you this
469 password you can assume they're in on the secret and can be trusted (ignore
470 identity theft for now). Checking the password, or any other proof is called
471 B<credential verification>.
473 By this time you know exactly who the user is - the user's identity is
474 B<authenticated>. This is where this module's job stops, and your application
475 or other plugins step in.
477 The next logical step is B<authorization>, the process of deciding what a user
478 is (or isn't) allowed to do. For example, say your users are split into two
479 main groups - regular users and administrators. You want to verify that the
480 currently logged in user is indeed an administrator before performing the
481 actions in an administrative part of your application. These decisionsmay be
482 made within your application code using just the information available after
483 authentication, or it may be facilitated by a number of plugins.
485 =head2 The Components In This Framework
489 Configuration of the Catalyst::Plugin::Authentication framework is done in
490 terms of realms. In simplest terms, a realm is a pairing of a Credential
491 verifier and a User storage (Store) backend.
493 An application can have any number of Realms, each of which operates
494 independant of the others. Each realm has a name, which is used to identify it
495 as the target of an authentication request. This name can be anything, such as
496 'users' or 'members'. One realm must be defined as the default_realm, which is
497 used when no realm name is specified. More information about configuring
498 realms is available in the configuration section.
500 =head3 Credential Verifiers
502 When user input is transferred to the L<Catalyst> application (typically via
503 form inputs) the application may pass this information into the authentication
504 system through the $c->authenticate() method. From there, it is passed to the
505 appropriate Credential verifier.
507 These plugins check the data, and ensure that it really proves the user is who
510 =head3 Storage Backends
512 The authentication data also identifies a user, and the Storage Backend modules
513 use this data to locate and return a standardized object-oriented
514 representation of a user.
516 When a user is retrieved from a store it is not necessarily authenticated.
517 Credential verifiers accept a set of authentication data and use this
518 information to retrieve the user from the store they are paired with.
520 =head3 The Core Plugin
522 This plugin on its own is the glue, providing realm configuration, session
523 integration, and other goodness for the other plugins.
527 More layers of plugins can be stacked on top of the authentication code. For
528 example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
529 browser sessions that is more persistent per users.
530 L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
531 and group users into categories, and then check which categories the current
536 Let's say we were storing users in a simple perl hash. Users are
537 verified by supplying a password which is matched within the hash.
539 This means that our application will begin like this:
547 __PACKAGE__->config->{authentication} =
549 default_realm => 'members',
559 password => "s00p3r",
561 roles => [qw/edit delete/],
564 password => "s3cr3t",
565 roles => [qw/comment/],
574 This tells the authentication plugin what realms are available, which
575 credential and store modules are used, and the configuration of each. With
576 this code loaded, we can now attempt to authenticate users.
578 To show an example of this, let's create an authentication controller:
580 package MyApp::Controller::Auth;
583 my ( $self, $c ) = @_;
585 if ( my $user = $c->req->param("user")
586 and my $password = $c->req->param("password") )
588 if ( $c->authenticate( { username => $user,
589 password => $password } ) ) {
590 $c->res->body( "hello " . $c->user->get("name") );
600 This code should be very readable. If all the necessary fields are supplied,
601 call the L<Catalyst::Plugin::Authentication/authenticate> method in the
602 controller. If it succeeds the user is logged in.
604 The credential verifier will attempt to retrieve the user whose details match
605 the authentication information provided to $c->authenticate(). Once it fetches
606 the user the password is checked and if it matches the user will be
607 B<authenticated> and C<< $c->user >> will contain the user object retrieved
610 In the above case, the default realm is checked, but we could just as easily
611 check an alternate realm. If this were an admin login, for example, we could
612 authenticate on the admin realm by simply changing the $c->authenticate()
615 if ( $c->authenticate( { username => $user,
616 password => $password }, 'admin' )l ) {
617 $c->res->body( "hello " . $c->user->get("name") );
621 Now suppose we want to restrict the ability to edit to a user with 'edit'
624 The restricted action might look like this:
627 my ( $self, $c ) = @_;
629 $c->detach("unauthorized")
630 unless $c->user_exists
631 and $c->user->get('editor') == 'yes';
633 # do something restricted here
636 This is somewhat similar to role based access control.
637 L<Catalyst::Plugin::Authentication::Store::Minimal> treats the roles field as
638 an array of role names. Let's leverage this. Add the role authorization
647 my ( $self, $c ) = @_;
649 $c->detach("unauthorized") unless $c->check_roles("edit");
651 # do something restricted here
654 This is somewhat simpler and will work if you change your store, too, since the
655 role interface is consistent.
657 Let's say your app grew, and you now have 10000 users. It's no longer
658 efficient to maintain a hash of users, so you move this data to a database.
659 You can accomplish this simply by installing the DBIx::Class Store and
660 changing your config:
662 __PACKAGE__->config->{authentication} =
664 default_realm => 'members',
671 class => 'DBIx::Class',
672 user_class => 'MyApp::Users',
673 role_column => 'roles'
679 The authentication system works behind the scenes to load your data from the
680 new source. The rest of your application is completely unchanged.
688 __PACKAGE__->config->{authentication} =
690 default_realm => 'members',
697 class => 'DBIx::Class',
698 user_class => 'MyApp::Users',
699 role_column => 'roles'
707 class => '+MyApp::Authentication::Store::NetAuth',
708 authserver => '192.168.10.17'
717 Whether or not to store the user's logged in state in the session, if the
718 application is also using L<Catalyst::Plugin::Session>. This
719 value is set to true per default.
723 This defines which realm should be used as when no realm is provided to methods
724 that require a realm such as authenticate or find_user.
728 This contains the series of realm configurations you want to use for your app.
729 The only rule here is that there must be at least one. A realm consists of a
730 name, which is used to reference the realm, a credential and a store.
732 Each realm config contains two hashes, one called 'credential' and one called
733 'store', each of which provide configuration details to the respective modules.
734 The contents of these hashes is specific to the module being used, with the
735 exception of the 'class' element, which tells the core Authentication module the
736 classname to instantiate.
738 The 'class' element follows the standard Catalyst mechanism of class
739 specification. If a class is prefixed with a +, it is assumed to be a complete
740 class name. Otherwise it is considered to be a portion of the class name. For
741 credentials, the classname 'B<Password>', for example, is expanded to
742 Catalyst::Plugin::Authentication::Credential::B<Password>. For stores, the
743 classname 'B<storename>' is expanded to:
744 Catalyst::Plugin::Authentication::Store::B<storename>::Backend.
754 =item authenticate( $userinfo, $realm )
756 Attempts to authenticate the user using the information in the $userinfo hash
757 reference using the realm $realm. $realm may be omitted, in which case the
758 default realm is checked.
762 Returns the currently logged in user or undef if there is none.
766 Returns true if a user is logged in right now. The difference between
767 user_exists and user is that user_exists will return true if a user is logged
768 in, even if it has not been retrieved from the storage backend. If you only
769 need to know if the user is logged in, depending on the storage mechanism this
770 can be much more efficient.
774 Logs the user out, Deletes the currently logged in user from $c->user and the session.
776 =item find_user( $userinfo, $realm )
778 Fetch a particular users details, matching the provided user info, from the realm
783 =head1 INTERNAL METHODS
785 These methods are for Catalyst::Plugin::Authentication B<INTERNAL USE> only.
786 Please do not use them in your own code, whether application or credential /
787 store modules. If you do, you will very likely get the nasty shock of having
788 to fix / rewrite your code when things change. They are documented here only
793 =item set_authenticated ( $user, $realmname )
795 Marks a user as authenticated. This is called from within the authenticate
796 routine when a credential returns a user. $realmname defaults to 'default'
798 =item auth_restore_user ( $user, $realmname )
800 Used to restore a user from the session. In most cases this is called without
801 arguments to restore the user via the session. Can be called with arguments
802 when restoring a user from some other method. Currently not used in this way.
804 =item save_user_in_session ( $user, $realmname )
806 Used to save the user in a session. Saves $user in session, marked as
807 originating in $realmname. Both arguments are required.
811 Returns a hashref containing realmname -> realm instance pairs. Realm
812 instances contain an instantiated store and credential object as the 'store'
813 and 'credential' elements, respectively
815 =item get_auth_realm ( $realmname )
817 Retrieves the realm instance for the realmname provided.
825 This list might not be up to date.
827 =head2 User Storage Backends
829 L<Catalyst::Plugin::Authentication::Store::Minimal>,
830 L<Catalyst::Plugin::Authentication::Store::DBIx::Class>,
832 =head2 Credential verification
834 L<Catalyst::Plugin::Authentication::Credential::Password>,
835 L<Catalyst::Plugin::Authentication::Credential::HTTP>,
836 L<Catalyst::Plugin::Authentication::Credential::TypeKey>
840 L<Catalyst::Plugin::Authorization::ACL>,
841 L<Catalyst::Plugin::Authorization::Roles>
843 =head2 Internals Documentation
845 L<Catalyst::Plugin::Authentication::Store>
849 L<Catalyst::Plugin::Session>,
850 L<Catalyst::Plugin::Session::PerUser>
852 =head1 DON'T SEE ALSO
854 This module along with its sub plugins deprecate a great number of other
855 modules. These include L<Catalyst::Plugin::Authentication::Simple>,
856 L<Catalyst::Plugin::Authentication::CDBI>.
858 At the time of writing these plugins have not yet been replaced or updated, but
859 should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
860 L<Catalyst::Plugin::Authentication::LDAP>,
861 L<Catalyst::Plugin::Authentication::CDBI::Basic>,
862 L<Catalyst::Plugin::Authentication::Basic::Remote>.
865 =head1 COMPATIBILITY ROUTINES
867 In version 0.10 of L<Catalyst::Plugin::Authentication>, the API
868 changed. For app developers, this change is fairly minor, but for
869 Credential and Store authors, the changes are significant.
871 Please see the documentation in version 0.09 of
872 Catalyst::Plugin::Authentication for a better understanding of how the old API
875 The items below are still present in the plugin, though using them is
876 deprecated. They remain only as a transition tool, for those sites which can
877 not yet be upgraded to use the new system due to local customizations or use
878 of Credential / Store modules that have not yet been updated to work with the
881 These routines should not be used in any application using realms
882 functionality or any of the methods described above. These are for reference
889 This method is used to initiate authentication and user retrieval. Technically
890 this is part of the old Password credential module, included here for
893 =item default_auth_store
895 Return the store whose name is 'default'.
897 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
898 or by using a Store plugin:
900 # load the Minimal authentication store.
901 use Catalyst qw/Authentication Authentication::Store::Minimal/;
903 Sets the default store to
904 L<Catalyst::Plugin::Authentication::Store::Minimal::Backend>.
906 =item get_auth_store $name
908 Return the store whose name is $name.
910 =item get_auth_store_name $store
912 Return the name of the store $store.
916 A hash keyed by name, with the stores registered in the app.
918 =item register_auth_stores %stores_by_name
920 Register stores into the application.
928 Yuval Kogman, C<nothingmuch@woobling.org>
934 Jay Kuri C<jayk@cpan.org>
936 =head1 COPYRIGHT & LICENSE
938 Copyright (c) 2005 the aforementioned authors. All rights
939 reserved. This program is free software; you can redistribute
940 it and/or modify it under the same terms as Perl itself.