1 package Catalyst::Plugin::Authentication;
3 use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
6 __PACKAGE__->mk_accessors(qw/_user/);
15 # this optimization breaks under Template::Toolkit
16 # use user_exists instead
19 # constant->import(have_want => eval { require Want });
22 our $VERSION = "0.10002";
24 sub set_authenticated {
25 my ( $c, $user, $realmname ) = @_;
28 $c->request->{user} = $user; # compatibility kludge
31 $realmname = 'default';
34 if ( $c->isa("Catalyst::Plugin::Session")
35 and $c->config->{authentication}{use_session}
36 and $user->supports("session") )
38 $c->save_user_in_session($user, $realmname);
40 $user->auth_realm($realmname);
42 $c->NEXT::set_authenticated($user, $realmname);
52 if ( defined($c->_user) ) {
55 return $c->auth_restore_user;
59 # change this to allow specification of a realm - to verify the user is part of that realm
60 # in addition to verifying that they exist.
63 return defined($c->_user) || defined($c->_user_in_session);
66 # works like user_exists - except only returns true if user
67 # exists AND is in the realm requested.
69 my ($c, $realmname) = @_;
71 if (defined($c->_user)) {
72 return ($c->_user->auth_realm eq $realmname);
73 } elsif (defined($c->_user_in_session)) {
74 return ($c->session->{__user_realm} eq $realmname);
80 sub save_user_in_session {
81 my ( $c, $user, $realmname ) = @_;
83 $c->session->{__user_realm} = $realmname;
85 # we want to ask the store for a user prepared for the session.
86 # but older modules split this functionality between the user and the
87 # store. We try the store first. If not, we use the old method.
88 my $realm = $c->get_auth_realm($realmname);
89 if ($realm->{'store'}->can('for_session')) {
90 $c->session->{__user} = $realm->{'store'}->for_session($c, $user);
92 $c->session->{__user} = $user->for_session;
102 $c->isa("Catalyst::Plugin::Session")
103 and $c->config->{authentication}{use_session}
104 and $c->session_is_valid
106 delete @{ $c->session }{qw/__user __user_realm/};
109 $c->NEXT::logout(@_);
113 my ( $c, $userinfo, $realmname ) = @_;
115 $realmname ||= 'default';
116 my $realm = $c->get_auth_realm($realmname);
117 if ( $realm->{'store'} ) {
118 return $realm->{'store'}->find_user($userinfo, $c);
120 $c->log->debug('find_user: unable to locate a store matching the requested realm');
125 sub _user_in_session {
129 $c->isa("Catalyst::Plugin::Session")
130 and $c->config->{authentication}{use_session}
131 and $c->session_is_valid;
133 return $c->session->{__user};
136 sub auth_restore_user {
137 my ( $c, $frozen_user, $realmname ) = @_;
139 $frozen_user ||= $c->_user_in_session;
140 return unless defined($frozen_user);
142 $realmname ||= $c->session->{__user_realm};
143 return unless $realmname; # FIXME die unless? This is an internal inconsistency
145 my $realm = $c->get_auth_realm($realmname);
146 $c->_user( my $user = $realm->{'store'}->from_session( $c, $frozen_user ) );
148 # this sets the realm the user originated in.
149 $user->auth_realm($realmname);
155 # we can't actually do our setup in setup because the model has not yet been loaded.
156 # So we have to trigger off of setup_finished. :-(
160 $app->_authentication_initialize();
161 $app->NEXT::setup(@_);
164 ## the actual initialization routine. whee.
165 sub _authentication_initialize {
168 ## let's avoid recreating / configuring everything if we have already done it, eh?
169 if ($app->can('_auth_realms')) { return };
171 ## make classdata where it is used.
172 $app->mk_classdata( '_auth_realms' => {});
174 my $cfg = $app->config->{'authentication'} ||= {};
176 $cfg->{use_session} = 1;
178 if (exists($cfg->{'realms'})) {
179 foreach my $realm (keys %{$cfg->{'realms'}}) {
180 $app->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
182 # if we have a 'default-realm' in the config hash and we don't already
183 # have a realm called 'default', we point default at the realm specified
184 if (exists($cfg->{'default_realm'}) && !$app->get_auth_realm('default')) {
185 $app->_set_default_auth_realm($cfg->{'default_realm'});
189 ## BACKWARDS COMPATIBILITY - if realms is not defined - then we are probably dealing
190 ## with an old-school config. The only caveat here is that we must add a classname
192 ## also - we have to treat {store} as {stores}{default} - because
193 ## while it is not a clear as a valid config in the docs, it
194 ## is functional with the old api. Whee!
195 if (exists($cfg->{'store'}) && !exists($cfg->{'stores'}{'default'})) {
196 $cfg->{'stores'}{'default'} = $cfg->{'store'};
199 foreach my $storename (keys %{$cfg->{'stores'}}) {
201 store => { class => $cfg->{'stores'}{$storename} },
203 $app->setup_auth_realm($storename, $realmcfg);
210 sub setup_auth_realm {
211 my ($app, $realmname, $config) = @_;
213 $app->log->debug("Setting up auth realm $realmname") if $app->debug;
214 if (!exists($config->{'store'}{'class'})) {
215 Carp::croak "Couldn't setup the authentication realm named '$realmname', no class defined";
219 my $storeclass = $config->{'store'}{'class'};
221 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
222 ## taken to mean C::P::A::Store::(specifiedclass)
223 if ($storeclass !~ /^\+(.*)$/ ) {
224 $storeclass = "Catalyst::Plugin::Authentication::Store::${storeclass}";
230 # a little niceness - since most systems seem to use the password credential class,
231 # if no credential class is specified we use password.
232 $config->{credential}{class} ||= '+Catalyst::Plugin::Authentication::Credential::Password';
234 my $credentialclass = $config->{'credential'}{'class'};
236 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
237 ## taken to mean C::P::A::Credential::(specifiedclass)
238 if ($credentialclass !~ /^\+(.*)$/ ) {
239 $credentialclass = "Catalyst::Plugin::Authentication::Credential::${credentialclass}";
241 $credentialclass = $1;
244 # if we made it here - we have what we need to load the classes;
245 Catalyst::Utils::ensure_class_loaded( $credentialclass );
246 Catalyst::Utils::ensure_class_loaded( $storeclass );
248 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
249 # of get_user and add it to the class. this is because the auth routines use find_user,
250 # and rely on it being present. (this avoids per-call checks)
251 if (!$storeclass->can('find_user')) {
253 *{"${storeclass}::find_user"} = sub {
254 my ($self, $info) = @_;
255 my @rest = @{$info->{rest}} if exists($info->{rest});
256 $self->get_user($info->{id}, @rest);
260 ## a little cruft to stay compatible with some poorly written stores / credentials
261 ## we'll remove this soon.
262 if ($storeclass->can('new')) {
263 $app->auth_realms->{$realmname}{'store'} = $storeclass->new($config->{'store'}, $app);
265 $app->log->error("THIS IS DEPRECATED: $storeclass has no new() method - Attempting to use uninstantiated");
266 $app->auth_realms->{$realmname}{'store'} = $storeclass;
268 if ($credentialclass->can('new')) {
269 $app->auth_realms->{$realmname}{'credential'} = $credentialclass->new($config->{'credential'}, $app);
271 $app->log->error("THIS IS DEPRECATED: $credentialclass has no new() method - Attempting to use uninstantiated");
272 $app->auth_realms->{$realmname}{'credential'} = $credentialclass;
278 return($self->_auth_realms);
282 my ($app, $realmname) = @_;
283 return $app->auth_realms->{$realmname};
287 # Very internal method. Vital Valuable Urgent, Do not touch on pain of death.
288 # Using this method just assigns the default realm to be the value associated
289 # with the realmname provided. It WILL overwrite any real realm called 'default'
290 # so can be very confusing if used improperly. It's used properly already.
291 # Translation: don't use it.
292 sub _set_default_auth_realm {
293 my ($app, $realmname) = @_;
295 if (exists($app->auth_realms->{$realmname})) {
296 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
298 return $app->get_auth_realm('default');
302 my ($app, $userinfo, $realmname) = @_;
305 $realmname = 'default';
308 my $realm = $app->get_auth_realm($realmname);
310 ## note to self - make authenticate throw an exception if realm is invalid.
312 if ($realm && exists($realm->{'credential'})) {
313 my $user = $realm->{'credential'}->authenticate($app, $realm->{store}, $userinfo);
315 $app->set_authenticated($user, $realmname);
319 $app->log->debug("The realm requested, '$realmname' does not exist," .
320 " or there is no credential associated with it.")
325 ## BACKWARDS COMPATIBILITY -- Warning: Here be monsters!
327 # What follows are backwards compatibility routines - for use with Stores and Credentials
328 # that have not been updated to work with C::P::Authentication v0.10.
329 # These are here so as to not break people's existing installations, but will go away
330 # in a future version.
332 # The old style of configuration only supports a single store, as each store module
333 # sets itself as the default store upon being loaded. This is the only supported
334 # 'compatibility' mode.
338 my ( $c, $uid, @rest ) = @_;
340 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
344 ## this should only be called when using old-style authentication plugins. IF this gets
345 ## called in a new-style config - it will OVERWRITE the store of your default realm. Don't do it.
346 ## also - this is a partial setup - because no credential is instantiated... in other words it ONLY
347 ## works with old-style auth plugins and C::P::Authentication in compatibility mode. Trying to combine
348 ## this with a realm-type config will probably crash your app.
349 sub default_auth_store {
352 if ( my $new = shift ) {
353 $self->auth_realms->{'default'}{'store'} = $new;
357 $storeclass = ref($new);
362 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
363 # of get_user and add it to the class. this is because the auth routines use find_user,
364 # and rely on it being present. (this avoids per-call checks)
365 if (!$storeclass->can('find_user')) {
367 *{"${storeclass}::find_user"} = sub {
368 my ($self, $info) = @_;
369 my @rest = @{$info->{rest}} if exists($info->{rest});
370 $self->get_user($info->{id}, @rest);
375 return $self->get_auth_realm('default')->{'store'};
378 ## BACKWARDS COMPATIBILITY
379 ## this only ever returns a hash containing 'default' - as that is the only
380 ## supported mode of calling this.
381 sub auth_store_names {
384 my %hash = ( $self->get_auth_realm('default')->{'store'} => 'default' );
388 my ( $self, $name ) = @_;
390 if ($name ne 'default') {
391 Carp::croak "get_auth_store called on non-default realm '$name'. Only default supported in compatibility mode";
393 $self->default_auth_store();
397 sub get_auth_store_name {
398 my ( $self, $store ) = @_;
402 # sub auth_stores is only used internally - here for completeness
406 my %hash = ( 'default' => $self->get_auth_realm('default')->{'store'});
417 Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
418 authentication framework.
427 $c->authenticate({ username => 'myusername',
428 password => 'mypassword' });
429 my $age = $c->user->get('age');
434 The authentication plugin provides generic user support for Catalyst apps. It
435 is the basis for both authentication (checking the user is who they claim to
436 be), and authorization (allowing the user to do what the system authorises
439 Using authentication is split into two parts. A Store is used to actually
440 store the user information, and can store any amount of data related to the
441 user. Credentials are used to verify users, using information from the store,
442 given data from the frontend. A Credential and a Store are paired to form a
443 'Realm'. A Catalyst application using the authentication framework must have
444 at least one realm, and may have several.
446 To implement authentication in a Catalyst application you need to add this
447 module, and specify at least one realm in the configuration.
449 Authentication data can also be stored in a session, if the application
450 is using the L<Catalyst::Plugin::Session> module.
452 B<NOTE> in version 0.10 of this module, the interface to this module changed.
453 Please see L</COMPATIBILITY ROUTINES> for more information.
457 =head2 The Authentication/Authorization Process
459 Web applications typically need to identify a user - to tell the user apart
460 from other users. This is usually done in order to display private information
461 that is only that user's business, or to limit access to the application so
462 that only certain entities can access certain parts.
464 This process is split up into several steps. First you ask the user to identify
465 themselves. At this point you can't be sure that the user is really who they
468 Then the user tells you who they are, and backs this claim with some piece of
469 information that only the real user could give you. For example, a password is
470 a secret that is known to both the user and you. When the user tells you this
471 password you can assume they're in on the secret and can be trusted (ignore
472 identity theft for now). Checking the password, or any other proof is called
473 B<credential verification>.
475 By this time you know exactly who the user is - the user's identity is
476 B<authenticated>. This is where this module's job stops, and your application
477 or other plugins step in.
479 The next logical step is B<authorization>, the process of deciding what a user
480 is (or isn't) allowed to do. For example, say your users are split into two
481 main groups - regular users and administrators. You want to verify that the
482 currently logged in user is indeed an administrator before performing the
483 actions in an administrative part of your application. These decisions may be
484 made within your application code using just the information available after
485 authentication, or it may be facilitated by a number of plugins.
487 =head2 The Components In This Framework
491 Configuration of the Catalyst::Plugin::Authentication framework is done in
492 terms of realms. In simplest terms, a realm is a pairing of a Credential
493 verifier and a User storage (Store) backend.
495 An application can have any number of Realms, each of which operates
496 independant of the others. Each realm has a name, which is used to identify it
497 as the target of an authentication request. This name can be anything, such as
498 'users' or 'members'. One realm must be defined as the default_realm, which is
499 used when no realm name is specified. More information about configuring
500 realms is available in the configuration section.
502 =head3 Credential Verifiers
504 When user input is transferred to the L<Catalyst> application (typically via
505 form inputs) the application may pass this information into the authentication
506 system through the $c->authenticate() method. From there, it is passed to the
507 appropriate Credential verifier.
509 These plugins check the data, and ensure that it really proves the user is who
512 =head3 Storage Backends
514 The authentication data also identifies a user, and the Storage backend modules
515 use this data to locate and return a standardized object-oriented
516 representation of a user.
518 When a user is retrieved from a store it is not necessarily authenticated.
519 Credential verifiers accept a set of authentication data and use this
520 information to retrieve the user from the store they are paired with.
522 =head3 The Core Plugin
524 This plugin on its own is the glue, providing realm configuration, session
525 integration, and other goodness for the other plugins.
529 More layers of plugins can be stacked on top of the authentication code. For
530 example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
531 browser sessions that is more persistent per users.
532 L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
533 and group users into categories, and then check which categories the current
538 Let's say we were storing users in a simple perl hash. Users are
539 verified by supplying a password which is matched within the hash.
541 This means that our application will begin like this:
549 __PACKAGE__->config->{authentication} =
551 default_realm => 'members',
556 password_field => 'password',
557 password_type => 'clear'
563 password => "s00p3r",
565 roles => [qw/edit delete/],
568 password => "s3cr3t",
569 roles => [qw/comment/],
578 This tells the authentication plugin what realms are available, which
579 credential and store modules are used, and the configuration of each. With
580 this code loaded, we can now attempt to authenticate users.
582 To show an example of this, let's create an authentication controller:
584 package MyApp::Controller::Auth;
587 my ( $self, $c ) = @_;
589 if ( my $user = $c->req->param("user")
590 and my $password = $c->req->param("password") )
592 if ( $c->authenticate( { username => $user,
593 password => $password } ) ) {
594 $c->res->body( "hello " . $c->user->get("name") );
604 This code should be very readable. If all the necessary fields are supplied,
605 call the "authenticate" method from the controller. If it succeeds the
608 The credential verifier will attempt to retrieve the user whose details match
609 the authentication information provided to $c->authenticate(). Once it fetches
610 the user the password is checked and if it matches the user will be
611 B<authenticated> and C<< $c->user >> will contain the user object retrieved
614 In the above case, the default realm is checked, but we could just as easily
615 check an alternate realm. If this were an admin login, for example, we could
616 authenticate on the admin realm by simply changing the $c->authenticate()
619 if ( $c->authenticate( { username => $user,
620 password => $password }, 'admin' )l ) {
621 $c->res->body( "hello " . $c->user->get("name") );
625 Now suppose we want to restrict the ability to edit to a user with an
626 'editor' value of yes.
628 The restricted action might look like this:
631 my ( $self, $c ) = @_;
633 $c->detach("unauthorized")
634 unless $c->user_exists
635 and $c->user->get('editor') eq 'yes';
637 # do something restricted here
640 (Note that if you have multiple realms, you can use $c->user_in_realm('realmname')
641 in place of $c->user_exists(); This will essentially perform the same
642 verification as user_exists, with the added requirement that if there is a
643 user, it must have come from the realm specified.)
645 The above example is somewhat similar to role based access control.
646 L<Catalyst::Plugin::Authentication::Store::Minimal> treats the roles field as
647 an array of role names. Let's leverage this. Add the role authorization
656 my ( $self, $c ) = @_;
658 $c->detach("unauthorized") unless $c->check_roles("edit");
660 # do something restricted here
663 This is somewhat simpler and will work if you change your store, too, since the
664 role interface is consistent.
666 Let's say your app grew, and you now have 10000 users. It's no longer
667 efficient to maintain a hash of users, so you move this data to a database.
668 You can accomplish this simply by installing the DBIx::Class Store and
669 changing your config:
671 __PACKAGE__->config->{authentication} =
673 default_realm => 'members',
678 password_field => 'password',
679 password_type => 'clear'
682 class => 'DBIx::Class',
683 user_class => 'MyApp::Users',
684 role_column => 'roles'
690 The authentication system works behind the scenes to load your data from the
691 new source. The rest of your application is completely unchanged.
699 __PACKAGE__->config->{authentication} =
701 default_realm => 'members',
706 password_field => 'password',
707 password_type => 'clear'
710 class => 'DBIx::Class',
711 user_class => 'MyApp::Users',
712 role_column => 'roles'
718 password_field => 'password',
719 password_type => 'clear'
722 class => '+MyApp::Authentication::Store::NetAuth',
723 authserver => '192.168.10.17'
732 Whether or not to store the user's logged in state in the session, if the
733 application is also using L<Catalyst::Plugin::Session>. This
734 value is set to true per default.
738 This defines which realm should be used as when no realm is provided to methods
739 that require a realm such as authenticate or find_user.
743 This contains the series of realm configurations you want to use for your app.
744 The only rule here is that there must be at least one. A realm consists of a
745 name, which is used to reference the realm, a credential and a store.
747 Each realm config contains two hashes, one called 'credential' and one called
748 'store', each of which provide configuration details to the respective modules.
749 The contents of these hashes is specific to the module being used, with the
750 exception of the 'class' element, which tells the core Authentication module the
751 classname to instantiate.
753 The 'class' element follows the standard Catalyst mechanism of class
754 specification. If a class is prefixed with a +, it is assumed to be a complete
755 class name. Otherwise it is considered to be a portion of the class name. For
756 credentials, the classname 'B<Password>', for example, is expanded to
757 Catalyst::Plugin::Authentication::Credential::B<Password>. For stores, the
758 classname 'B<storename>' is expanded to:
759 Catalyst::Plugin::Authentication::Store::B<storename>.
769 =item authenticate( $userinfo, $realm )
771 Attempts to authenticate the user using the information in the $userinfo hash
772 reference using the realm $realm. $realm may be omitted, in which case the
773 default realm is checked.
777 Returns the currently logged in user or undef if there is none.
781 Returns true if a user is logged in right now. The difference between
782 user_exists and user is that user_exists will return true if a user is logged
783 in, even if it has not been yet retrieved from the storage backend. If you only
784 need to know if the user is logged in, depending on the storage mechanism this
785 can be much more efficient.
787 =item user_in_realm ( $realm )
789 Works like user_exists, except that it only returns true if a user is both
790 logged in right now and was retrieved from the realm provided.
794 Logs the user out, Deletes the currently logged in user from $c->user and the session.
796 =item find_user( $userinfo, $realm )
798 Fetch a particular users details, matching the provided user info, from the realm
803 =head1 INTERNAL METHODS
805 These methods are for Catalyst::Plugin::Authentication B<INTERNAL USE> only.
806 Please do not use them in your own code, whether application or credential /
807 store modules. If you do, you will very likely get the nasty shock of having
808 to fix / rewrite your code when things change. They are documented here only
813 =item set_authenticated ( $user, $realmname )
815 Marks a user as authenticated. This is called from within the authenticate
816 routine when a credential returns a user. $realmname defaults to 'default'
818 =item auth_restore_user ( $user, $realmname )
820 Used to restore a user from the session. In most cases this is called without
821 arguments to restore the user via the session. Can be called with arguments
822 when restoring a user from some other method. Currently not used in this way.
824 =item save_user_in_session ( $user, $realmname )
826 Used to save the user in a session. Saves $user in session, marked as
827 originating in $realmname. Both arguments are required.
831 Returns a hashref containing realmname -> realm instance pairs. Realm
832 instances contain an instantiated store and credential object as the 'store'
833 and 'credential' elements, respectively
835 =item get_auth_realm ( $realmname )
837 Retrieves the realm instance for the realmname provided.
845 This list might not be up to date. Below are modules known to work with the updated
846 API of 0.10 and are therefore compatible with realms.
848 =head2 User Storage Backends
850 L<Catalyst::Plugin::Authentication::Store::Minimal>,
851 L<Catalyst::Plugin::Authentication::Store::DBIx::Class>,
853 =head2 Credential verification
855 L<Catalyst::Plugin::Authentication::Credential::Password>,
859 L<Catalyst::Plugin::Authorization::ACL>,
860 L<Catalyst::Plugin::Authorization::Roles>
862 =head2 Internals Documentation
864 L<Catalyst::Plugin::Authentication::Internals>
868 L<Catalyst::Plugin::Session>,
869 L<Catalyst::Plugin::Session::PerUser>
871 =head1 DON'T SEE ALSO
873 This module along with its sub plugins deprecate a great number of other
874 modules. These include L<Catalyst::Plugin::Authentication::Simple>,
875 L<Catalyst::Plugin::Authentication::CDBI>.
877 At the time of writing these plugins have not yet been replaced or updated, but
878 should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
879 L<Catalyst::Plugin::Authentication::LDAP>,
880 L<Catalyst::Plugin::Authentication::CDBI::Basic>,
881 L<Catalyst::Plugin::Authentication::Basic::Remote>.
883 =head1 INCOMPATABILITIES
885 The realms based configuration and functionality of the 0.10 update
886 of L<Catalyst::Plugin::Authentication> required a change in the API used by
887 credentials and stores. It has a compatibility mode which allows use of
888 modules that have not yet been updated. This, however, completely mimics the
889 older api and disables the new realm-based features. In other words you can
890 not mix the older credential and store modules with realms, or realm-based
891 configs. The changes required to update modules are relatively minor and are
892 covered in L<Catalyst::Plugin::Authentication::Internals>. We hope that most
893 modules will move to the compatible list above very quickly.
895 =head1 COMPATIBILITY ROUTINES
897 In version 0.10 of L<Catalyst::Plugin::Authentication>, the API
898 changed. For app developers, this change is fairly minor, but for
899 Credential and Store authors, the changes are significant.
901 Please see the documentation in version 0.09 of
902 Catalyst::Plugin::Authentication for a better understanding of how the old API
905 The items below are still present in the plugin, though using them is
906 deprecated. They remain only as a transition tool, for those sites which can
907 not yet be upgraded to use the new system due to local customizations or use
908 of Credential / Store modules that have not yet been updated to work with the
911 These routines should not be used in any application using realms
912 functionality or any of the methods described above. These are for reference
919 This method is used to initiate authentication and user retrieval. Technically
920 this is part of the old Password credential module and it still resides in the
921 L<Password|Catalyst::Plugin::Authentication::Credential::Password> class. It is
922 included here for reference only.
924 =item default_auth_store
926 Return the store whose name is 'default'.
928 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
929 or by using a Store plugin:
931 # load the Minimal authentication store.
932 use Catalyst qw/Authentication Authentication::Store::Minimal/;
934 Sets the default store to
935 L<Catalyst::Plugin::Authentication::Store::Minimal>.
937 =item get_auth_store $name
939 Return the store whose name is $name.
941 =item get_auth_store_name $store
943 Return the name of the store $store.
947 A hash keyed by name, with the stores registered in the app.
949 =item register_auth_stores %stores_by_name
951 Register stores into the application.
959 Yuval Kogman, C<nothingmuch@woobling.org>
961 Jay Kuri, C<jayk@cpan.org>
968 =head1 COPYRIGHT & LICENSE
970 Copyright (c) 2005 the aforementioned authors. All rights
971 reserved. This program is free software; you can redistribute
972 it and/or modify it under the same terms as Perl itself.