3 package Catalyst::Plugin::Authentication;
5 use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
8 __PACKAGE__->mk_accessors(qw/_user/);
17 # this optimization breaks under Template::Toolkit
18 # use user_exists instead
21 # constant->import(have_want => eval { require Want });
24 our $VERSION = "0.10002";
26 sub set_authenticated {
27 my ( $c, $user, $realmname ) = @_;
30 $c->request->{user} = $user; # compatibility kludge
33 $realmname = 'default';
36 if ( $c->isa("Catalyst::Plugin::Session")
37 and $c->config->{authentication}{use_session}
38 and $user->supports("session") )
40 $c->save_user_in_session($user, $realmname);
42 $user->auth_realm($realmname);
44 $c->NEXT::set_authenticated($user, $realmname);
54 if ( defined($c->_user) ) {
57 return $c->auth_restore_user;
61 # change this to allow specification of a realm - to verify the user is part of that realm
62 # in addition to verifying that they exist.
65 return defined($c->_user) || defined($c->_user_in_session);
68 # works like user_exists - except only returns true if user
69 # exists AND is in the realm requested.
71 my ($c, $realmname) = @_;
73 if (defined($c->_user)) {
74 return ($c->_user->auth_realm eq $realmname);
75 } elsif (defined($c->_user_in_session)) {
76 return ($c->session->{__user_realm} eq $realmname);
82 sub save_user_in_session {
83 my ( $c, $user, $realmname ) = @_;
85 $c->session->{__user_realm} = $realmname;
87 # we want to ask the store for a user prepared for the session.
88 # but older modules split this functionality between the user and the
89 # store. We try the store first. If not, we use the old method.
90 my $realm = $c->get_auth_realm($realmname);
91 if ($realm->{'store'}->can('for_session')) {
92 $c->session->{__user} = $realm->{'store'}->for_session($c, $user);
94 $c->session->{__user} = $user->for_session;
104 $c->isa("Catalyst::Plugin::Session")
105 and $c->config->{authentication}{use_session}
106 and $c->session_is_valid
108 delete @{ $c->session }{qw/__user __user_realm/};
111 $c->NEXT::logout(@_);
115 my ( $c, $userinfo, $realmname ) = @_;
117 $realmname ||= 'default';
118 my $realm = $c->get_auth_realm($realmname);
119 if ( $realm->{'store'} ) {
120 return $realm->{'store'}->find_user($userinfo, $c);
122 $c->log->debug('find_user: unable to locate a store matching the requested realm');
127 sub _user_in_session {
131 $c->isa("Catalyst::Plugin::Session")
132 and $c->config->{authentication}{use_session}
133 and $c->session_is_valid;
135 return $c->session->{__user};
138 sub auth_restore_user {
139 my ( $c, $frozen_user, $realmname ) = @_;
141 $frozen_user ||= $c->_user_in_session;
142 return unless defined($frozen_user);
144 $realmname ||= $c->session->{__user_realm};
145 return unless $realmname; # FIXME die unless? This is an internal inconsistency
147 my $realm = $c->get_auth_realm($realmname);
148 $c->_user( my $user = $realm->{'store'}->from_session( $c, $frozen_user ) );
150 # this sets the realm the user originated in.
151 $user->auth_realm($realmname);
157 # we can't actually do our setup in setup because the model has not yet been loaded.
158 # So we have to trigger off of setup_finished. :-(
162 $app->_authentication_initialize();
163 $app->NEXT::setup(@_);
166 ## the actual initialization routine. whee.
167 sub _authentication_initialize {
170 ## let's avoid recreating / configuring everything if we have already done it, eh?
171 if ($app->can('_auth_realms')) { return };
173 ## make classdata where it is used.
174 $app->mk_classdata( '_auth_realms' => {});
176 my $cfg = $app->config->{'authentication'} ||= {};
178 $cfg->{use_session} = 1;
180 if (exists($cfg->{'realms'})) {
181 foreach my $realm (keys %{$cfg->{'realms'}}) {
182 $app->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
184 # if we have a 'default-realm' in the config hash and we don't already
185 # have a realm called 'default', we point default at the realm specified
186 if (exists($cfg->{'default_realm'}) && !$app->get_auth_realm('default')) {
187 $app->_set_default_auth_realm($cfg->{'default_realm'});
191 ## BACKWARDS COMPATIBILITY - if realms is not defined - then we are probably dealing
192 ## with an old-school config. The only caveat here is that we must add a classname
194 ## also - we have to treat {store} as {stores}{default} - because
195 ## while it is not a clear as a valid config in the docs, it
196 ## is functional with the old api. Whee!
197 if (exists($cfg->{'store'}) && !exists($cfg->{'stores'}{'default'})) {
198 $cfg->{'stores'}{'default'} = $cfg->{'store'};
201 foreach my $storename (keys %{$cfg->{'stores'}}) {
203 store => { class => $cfg->{'stores'}{$storename} },
205 $app->setup_auth_realm($storename, $realmcfg);
212 sub setup_auth_realm {
213 my ($app, $realmname, $config) = @_;
215 $app->log->debug("Setting up auth realm $realmname") if $app->debug;
216 if (!exists($config->{'store'}{'class'})) {
217 Carp::croak "Couldn't setup the authentication realm named '$realmname', no class defined";
221 my $storeclass = $config->{'store'}{'class'};
223 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
224 ## taken to mean C::P::A::Store::(specifiedclass)
225 if ($storeclass !~ /^\+(.*)$/ ) {
226 $storeclass = "Catalyst::Plugin::Authentication::Store::${storeclass}";
232 # a little niceness - since most systems seem to use the password credential class,
233 # if no credential class is specified we use password.
234 $config->{credential}{class} ||= '+Catalyst::Plugin::Authentication::Credential::Password';
236 my $credentialclass = $config->{'credential'}{'class'};
238 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
239 ## taken to mean C::P::A::Credential::(specifiedclass)
240 if ($credentialclass !~ /^\+(.*)$/ ) {
241 $credentialclass = "Catalyst::Plugin::Authentication::Credential::${credentialclass}";
243 $credentialclass = $1;
246 # if we made it here - we have what we need to load the classes;
247 Catalyst::Utils::ensure_class_loaded( $credentialclass );
248 Catalyst::Utils::ensure_class_loaded( $storeclass );
250 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
251 # of get_user and add it to the class. this is because the auth routines use find_user,
252 # and rely on it being present. (this avoids per-call checks)
253 if (!$storeclass->can('find_user')) {
255 *{"${storeclass}::find_user"} = sub {
256 my ($self, $info) = @_;
257 my @rest = @{$info->{rest}} if exists($info->{rest});
258 $self->get_user($info->{id}, @rest);
262 ## a little cruft to stay compatible with some poorly written stores / credentials
263 ## we'll remove this soon.
264 if ($storeclass->can('new')) {
265 $app->auth_realms->{$realmname}{'store'} = $storeclass->new($config->{'store'}, $app);
267 $app->log->error("THIS IS DEPRECATED: $storeclass has no new() method - Attempting to use uninstantiated");
268 $app->auth_realms->{$realmname}{'store'} = $storeclass;
270 if ($credentialclass->can('new')) {
271 $app->auth_realms->{$realmname}{'credential'} = $credentialclass->new($config->{'credential'}, $app);
273 $app->log->error("THIS IS DEPRECATED: $credentialclass has no new() method - Attempting to use uninstantiated");
274 $app->auth_realms->{$realmname}{'credential'} = $credentialclass;
280 return($self->_auth_realms);
284 my ($app, $realmname) = @_;
285 return $app->auth_realms->{$realmname};
289 # Very internal method. Vital Valuable Urgent, Do not touch on pain of death.
290 # Using this method just assigns the default realm to be the value associated
291 # with the realmname provided. It WILL overwrite any real realm called 'default'
292 # so can be very confusing if used improperly. It's used properly already.
293 # Translation: don't use it.
294 sub _set_default_auth_realm {
295 my ($app, $realmname) = @_;
297 if (exists($app->auth_realms->{$realmname})) {
298 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
300 return $app->get_auth_realm('default');
304 my ($app, $userinfo, $realmname) = @_;
307 $realmname = 'default';
310 my $realm = $app->get_auth_realm($realmname);
312 ## note to self - make authenticate throw an exception if realm is invalid.
314 if ($realm && exists($realm->{'credential'})) {
315 my $user = $realm->{'credential'}->authenticate($app, $realm->{store}, $userinfo);
317 $app->set_authenticated($user, $realmname);
321 $app->log->debug("The realm requested, '$realmname' does not exist," .
322 " or there is no credential associated with it.")
327 ## BACKWARDS COMPATIBILITY -- Warning: Here be monsters!
329 # What follows are backwards compatibility routines - for use with Stores and Credentials
330 # that have not been updated to work with C::P::Authentication v0.10.
331 # These are here so as to not break people's existing installations, but will go away
332 # in a future version.
334 # The old style of configuration only supports a single store, as each store module
335 # sets itself as the default store upon being loaded. This is the only supported
336 # 'compatibility' mode.
340 my ( $c, $uid, @rest ) = @_;
342 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
346 ## this should only be called when using old-style authentication plugins. IF this gets
347 ## called in a new-style config - it will OVERWRITE the store of your default realm. Don't do it.
348 ## also - this is a partial setup - because no credential is instantiated... in other words it ONLY
349 ## works with old-style auth plugins and C::P::Authentication in compatibility mode. Trying to combine
350 ## this with a realm-type config will probably crash your app.
351 sub default_auth_store {
354 if ( my $new = shift ) {
355 $self->auth_realms->{'default'}{'store'} = $new;
359 $storeclass = ref($new);
364 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
365 # of get_user and add it to the class. this is because the auth routines use find_user,
366 # and rely on it being present. (this avoids per-call checks)
367 if (!$storeclass->can('find_user')) {
369 *{"${storeclass}::find_user"} = sub {
370 my ($self, $info) = @_;
371 my @rest = @{$info->{rest}} if exists($info->{rest});
372 $self->get_user($info->{id}, @rest);
377 return $self->get_auth_realm('default')->{'store'};
380 ## BACKWARDS COMPATIBILITY
381 ## this only ever returns a hash containing 'default' - as that is the only
382 ## supported mode of calling this.
383 sub auth_store_names {
386 my %hash = ( $self->get_auth_realm('default')->{'store'} => 'default' );
390 my ( $self, $name ) = @_;
392 if ($name ne 'default') {
393 Carp::croak "get_auth_store called on non-default realm '$name'. Only default supported in compatibility mode";
395 $self->default_auth_store();
399 sub get_auth_store_name {
400 my ( $self, $store ) = @_;
404 # sub auth_stores is only used internally - here for completeness
408 my %hash = ( 'default' => $self->get_auth_realm('default')->{'store'});
419 Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
420 authentication framework.
429 $c->authenticate({ username => 'myusername',
430 password => 'mypassword' });
431 my $age = $c->user->get('age');
436 The authentication plugin provides generic user support for Catalyst apps. It
437 is the basis for both authentication (checking the user is who they claim to
438 be), and authorization (allowing the user to do what the system authorises
441 Using authentication is split into two parts. A Store is used to actually
442 store the user information, and can store any amount of data related to the
443 user. Credentials are used to verify users, using information from the store,
444 given data from the frontend. A Credential and a Store are paired to form a
445 'Realm'. A Catalyst application using the authentication framework must have
446 at least one realm, and may have several.
448 To implement authentication in a Catalyst application you need to add this
449 module, and specify at least one realm in the configuration.
451 Authentication data can also be stored in a session, if the application
452 is using the L<Catalyst::Plugin::Session> module.
454 B<NOTE> in version 0.10 of this module, the interface to this module changed.
455 Please see L</COMPATIBILITY ROUTINES> for more information.
459 =head2 The Authentication/Authorization Process
461 Web applications typically need to identify a user - to tell the user apart
462 from other users. This is usually done in order to display private information
463 that is only that user's business, or to limit access to the application so
464 that only certain entities can access certain parts.
466 This process is split up into several steps. First you ask the user to identify
467 themselves. At this point you can't be sure that the user is really who they
470 Then the user tells you who they are, and backs this claim with some piece of
471 information that only the real user could give you. For example, a password is
472 a secret that is known to both the user and you. When the user tells you this
473 password you can assume they're in on the secret and can be trusted (ignore
474 identity theft for now). Checking the password, or any other proof is called
475 B<credential verification>.
477 By this time you know exactly who the user is - the user's identity is
478 B<authenticated>. This is where this module's job stops, and your application
479 or other plugins step in.
481 The next logical step is B<authorization>, the process of deciding what a user
482 is (or isn't) allowed to do. For example, say your users are split into two
483 main groups - regular users and administrators. You want to verify that the
484 currently logged in user is indeed an administrator before performing the
485 actions in an administrative part of your application. These decisions may be
486 made within your application code using just the information available after
487 authentication, or it may be facilitated by a number of plugins.
489 =head2 The Components In This Framework
493 Configuration of the Catalyst::Plugin::Authentication framework is done in
494 terms of realms. In simplest terms, a realm is a pairing of a Credential
495 verifier and a User storage (Store) backend.
497 An application can have any number of Realms, each of which operates
498 independant of the others. Each realm has a name, which is used to identify it
499 as the target of an authentication request. This name can be anything, such as
500 'users' or 'members'. One realm must be defined as the default_realm, which is
501 used when no realm name is specified. More information about configuring
502 realms is available in the configuration section.
504 =head3 Credential Verifiers
506 When user input is transferred to the L<Catalyst> application (typically via
507 form inputs) the application may pass this information into the authentication
508 system through the $c->authenticate() method. From there, it is passed to the
509 appropriate Credential verifier.
511 These plugins check the data, and ensure that it really proves the user is who
514 =head3 Storage Backends
516 The authentication data also identifies a user, and the Storage backend modules
517 use this data to locate and return a standardized object-oriented
518 representation of a user.
520 When a user is retrieved from a store it is not necessarily authenticated.
521 Credential verifiers accept a set of authentication data and use this
522 information to retrieve the user from the store they are paired with.
524 =head3 The Core Plugin
526 This plugin on its own is the glue, providing realm configuration, session
527 integration, and other goodness for the other plugins.
531 More layers of plugins can be stacked on top of the authentication code. For
532 example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
533 browser sessions that is more persistent per users.
534 L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
535 and group users into categories, and then check which categories the current
540 Let's say we were storing users in a simple perl hash. Users are
541 verified by supplying a password which is matched within the hash.
543 This means that our application will begin like this:
551 __PACKAGE__->config->{authentication} =
553 default_realm => 'members',
558 password_field => 'password',
559 password_type => 'clear'
565 password => "s00p3r",
567 roles => [qw/edit delete/],
570 password => "s3cr3t",
571 roles => [qw/comment/],
580 This tells the authentication plugin what realms are available, which
581 credential and store modules are used, and the configuration of each. With
582 this code loaded, we can now attempt to authenticate users.
584 To show an example of this, let's create an authentication controller:
586 package MyApp::Controller::Auth;
589 my ( $self, $c ) = @_;
591 if ( my $user = $c->req->param("user")
592 and my $password = $c->req->param("password") )
594 if ( $c->authenticate( { username => $user,
595 password => $password } ) ) {
596 $c->res->body( "hello " . $c->user->get("name") );
606 This code should be very readable. If all the necessary fields are supplied,
607 call the "authenticate" method from the controller. If it succeeds the
610 The credential verifier will attempt to retrieve the user whose details match
611 the authentication information provided to $c->authenticate(). Once it fetches
612 the user the password is checked and if it matches the user will be
613 B<authenticated> and C<< $c->user >> will contain the user object retrieved
616 In the above case, the default realm is checked, but we could just as easily
617 check an alternate realm. If this were an admin login, for example, we could
618 authenticate on the admin realm by simply changing the $c->authenticate()
621 if ( $c->authenticate( { username => $user,
622 password => $password }, 'admin' )l ) {
623 $c->res->body( "hello " . $c->user->get("name") );
627 Now suppose we want to restrict the ability to edit to a user with an
628 'editor' value of yes.
630 The restricted action might look like this:
633 my ( $self, $c ) = @_;
635 $c->detach("unauthorized")
636 unless $c->user_exists
637 and $c->user->get('editor') eq 'yes';
639 # do something restricted here
642 (Note that if you have multiple realms, you can use $c->user_in_realm('realmname')
643 in place of $c->user_exists(); This will essentially perform the same
644 verification as user_exists, with the added requirement that if there is a
645 user, it must have come from the realm specified.)
647 The above example is somewhat similar to role based access control.
648 L<Catalyst::Plugin::Authentication::Store::Minimal> treats the roles field as
649 an array of role names. Let's leverage this. Add the role authorization
658 my ( $self, $c ) = @_;
660 $c->detach("unauthorized") unless $c->check_roles("edit");
662 # do something restricted here
665 This is somewhat simpler and will work if you change your store, too, since the
666 role interface is consistent.
668 Let's say your app grew, and you now have 10000 users. It's no longer
669 efficient to maintain a hash of users, so you move this data to a database.
670 You can accomplish this simply by installing the DBIx::Class Store and
671 changing your config:
673 __PACKAGE__->config->{authentication} =
675 default_realm => 'members',
680 password_field => 'password',
681 password_type => 'clear'
684 class => 'DBIx::Class',
685 user_class => 'MyApp::Users',
686 role_column => 'roles'
692 The authentication system works behind the scenes to load your data from the
693 new source. The rest of your application is completely unchanged.
701 __PACKAGE__->config->{authentication} =
703 default_realm => 'members',
708 password_field => 'password',
709 password_type => 'clear'
712 class => 'DBIx::Class',
713 user_class => 'MyApp::Users',
714 role_column => 'roles'
720 password_field => 'password',
721 password_type => 'clear'
724 class => '+MyApp::Authentication::Store::NetAuth',
725 authserver => '192.168.10.17'
734 Whether or not to store the user's logged in state in the session, if the
735 application is also using L<Catalyst::Plugin::Session>. This
736 value is set to true per default.
740 This defines which realm should be used as when no realm is provided to methods
741 that require a realm such as authenticate or find_user.
745 This contains the series of realm configurations you want to use for your app.
746 The only rule here is that there must be at least one. A realm consists of a
747 name, which is used to reference the realm, a credential and a store.
749 Each realm config contains two hashes, one called 'credential' and one called
750 'store', each of which provide configuration details to the respective modules.
751 The contents of these hashes is specific to the module being used, with the
752 exception of the 'class' element, which tells the core Authentication module the
753 classname to instantiate.
755 The 'class' element follows the standard Catalyst mechanism of class
756 specification. If a class is prefixed with a +, it is assumed to be a complete
757 class name. Otherwise it is considered to be a portion of the class name. For
758 credentials, the classname 'B<Password>', for example, is expanded to
759 Catalyst::Plugin::Authentication::Credential::B<Password>. For stores, the
760 classname 'B<storename>' is expanded to:
761 Catalyst::Plugin::Authentication::Store::B<storename>.
771 =item authenticate( $userinfo, $realm )
773 Attempts to authenticate the user using the information in the $userinfo hash
774 reference using the realm $realm. $realm may be omitted, in which case the
775 default realm is checked.
779 Returns the currently logged in user or undef if there is none.
783 Returns true if a user is logged in right now. The difference between
784 user_exists and user is that user_exists will return true if a user is logged
785 in, even if it has not been yet retrieved from the storage backend. If you only
786 need to know if the user is logged in, depending on the storage mechanism this
787 can be much more efficient.
789 =item user_in_realm ( $realm )
791 Works like user_exists, except that it only returns true if a user is both
792 logged in right now and was retrieved from the realm provided.
796 Logs the user out, Deletes the currently logged in user from $c->user and the session.
798 =item find_user( $userinfo, $realm )
800 Fetch a particular users details, matching the provided user info, from the realm
805 =head1 INTERNAL METHODS
807 These methods are for Catalyst::Plugin::Authentication B<INTERNAL USE> only.
808 Please do not use them in your own code, whether application or credential /
809 store modules. If you do, you will very likely get the nasty shock of having
810 to fix / rewrite your code when things change. They are documented here only
815 =item set_authenticated ( $user, $realmname )
817 Marks a user as authenticated. This is called from within the authenticate
818 routine when a credential returns a user. $realmname defaults to 'default'
820 =item auth_restore_user ( $user, $realmname )
822 Used to restore a user from the session. In most cases this is called without
823 arguments to restore the user via the session. Can be called with arguments
824 when restoring a user from some other method. Currently not used in this way.
826 =item save_user_in_session ( $user, $realmname )
828 Used to save the user in a session. Saves $user in session, marked as
829 originating in $realmname. Both arguments are required.
833 Returns a hashref containing realmname -> realm instance pairs. Realm
834 instances contain an instantiated store and credential object as the 'store'
835 and 'credential' elements, respectively
837 =item get_auth_realm ( $realmname )
839 Retrieves the realm instance for the realmname provided.
847 This list might not be up to date. Below are modules known to work with the updated
848 API of 0.10 and are therefore compatible with realms.
850 =head2 User Storage Backends
852 L<Catalyst::Plugin::Authentication::Store::Minimal>,
853 L<Catalyst::Plugin::Authentication::Store::DBIx::Class>,
855 =head2 Credential verification
857 L<Catalyst::Plugin::Authentication::Credential::Password>,
861 L<Catalyst::Plugin::Authorization::ACL>,
862 L<Catalyst::Plugin::Authorization::Roles>
864 =head2 Internals Documentation
866 L<Catalyst::Plugin::Authentication::Internals>
870 L<Catalyst::Plugin::Session>,
871 L<Catalyst::Plugin::Session::PerUser>
873 =head1 DON'T SEE ALSO
875 This module along with its sub plugins deprecate a great number of other
876 modules. These include L<Catalyst::Plugin::Authentication::Simple>,
877 L<Catalyst::Plugin::Authentication::CDBI>.
879 At the time of writing these plugins have not yet been replaced or updated, but
880 should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
881 L<Catalyst::Plugin::Authentication::LDAP>,
882 L<Catalyst::Plugin::Authentication::CDBI::Basic>,
883 L<Catalyst::Plugin::Authentication::Basic::Remote>.
885 =head1 INCOMPATABILITIES
887 The realms based configuration and functionality of the 0.10 update
888 of L<Catalyst::Plugin::Authentication> required a change in the API used by
889 credentials and stores. It has a compatibility mode which allows use of
890 modules that have not yet been updated. This, however, completely mimics the
891 older api and disables the new realm-based features. In other words you can
892 not mix the older credential and store modules with realms, or realm-based
893 configs. The changes required to update modules are relatively minor and are
894 covered in L<Catalyst::Plugin::Authentication::Internals>. We hope that most
895 modules will move to the compatible list above very quickly.
897 =head1 COMPATIBILITY ROUTINES
899 In version 0.10 of L<Catalyst::Plugin::Authentication>, the API
900 changed. For app developers, this change is fairly minor, but for
901 Credential and Store authors, the changes are significant.
903 Please see the documentation in version 0.09 of
904 Catalyst::Plugin::Authentication for a better understanding of how the old API
907 The items below are still present in the plugin, though using them is
908 deprecated. They remain only as a transition tool, for those sites which can
909 not yet be upgraded to use the new system due to local customizations or use
910 of Credential / Store modules that have not yet been updated to work with the
913 These routines should not be used in any application using realms
914 functionality or any of the methods described above. These are for reference
921 This method is used to initiate authentication and user retrieval. Technically
922 this is part of the old Password credential module and it still resides in the
923 L<Password|Catalyst::Plugin::Authentication::Credential::Password> class. It is
924 included here for reference only.
926 =item default_auth_store
928 Return the store whose name is 'default'.
930 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
931 or by using a Store plugin:
933 # load the Minimal authentication store.
934 use Catalyst qw/Authentication Authentication::Store::Minimal/;
936 Sets the default store to
937 L<Catalyst::Plugin::Authentication::Store::Minimal>.
939 =item get_auth_store $name
941 Return the store whose name is $name.
943 =item get_auth_store_name $store
945 Return the name of the store $store.
949 A hash keyed by name, with the stores registered in the app.
951 =item register_auth_stores %stores_by_name
953 Register stores into the application.
961 Yuval Kogman, C<nothingmuch@woobling.org>
963 Jay Kuri, C<jayk@cpan.org>
970 =head1 COPYRIGHT & LICENSE
972 Copyright (c) 2005 the aforementioned authors. All rights
973 reserved. This program is free software; you can redistribute
974 it and/or modify it under the same terms as Perl itself.