There are two main entry points you need to be aware of when writing a store
or credential module. The first is initialization and the second is during the
-actual call to the Catalyst application's authenticate method.
+actual call to the Catalyst application's authenticate method.
+
+A simplified description of the authentication process follows:
+
+
+
+B<Initialization>
+
+=over 4
+
+B<Realm Setup> - for each realm:
+
+=over 4
+
+1) The Realm is instantiated using new() method
+
+2) The Store is instantiated using new() method
+
+3) The Credential Instantiated using new() method
+
+4) Credential and Store objects tied to realm for use during requests
+
+=back
+
+=back
+
+B<Authentication>
+
+=over 4
+
+C<< $c->authenticate( $userinfo, $realm ) >> called
+
+=over 4
+
+1) Credential object retrieved for realm provided
+
+2) Credential's authenticate() method called with authinfo and realm object for current realm
+
+=over 4
+
+The realm object and the authinfo hash are provided to the credential object's
+authenticate call. In most cases the credential object will attempt to
+retrieve a user using the realm's find_user() method, which by default relays
+the call directly to the Store's find_user() method. It will then usually
+compare the retrieved user's information with the information provided in the
+$authinfo hash. This is how the default 'Password' credential functions. If
+the credentials match, the authenticate() method should return a user object.
+
+=back
+
+3) User object stored in session
+
+=over 4
+
+If the user object supports session storage, the successfully authenticated
+user will be placed in session storage. This is done by calling the realm
+object's persist_user() method. The persist_user() routine by
+default calls the Store's for_session() method, which should return serialized
+data (IE a scalar). This serialized data is passed back to the store via the
+from_session() method, so the data should contain enough information for the
+store to recreate / reload the user.
+
+=back
+
+=back
+
+=back
+
+B<Sessions> - Per-Request operations
+
+=over 4
+
+When any user-related activity occurs, and $c->authenticate has not
+yet been called, the Catalyst::Plugin::Authentication module will
+attempt to restore the persisted user (normally from the session if one is available).
+There is only one step in this process:
+
+=over 4
+
+1) Store object's from_session() is called
+
+=back
+
+The serialized data previously returned by the store's for_session()
+method is provided to the from_session() method. The from_session()
+method should return a valid user object.
+
+Note that the for_session() is only called during the original
+$c->authenticate() call, so if changes are made to the user that need
+to be reflected in your session data, you will want to call the
+$c->persist_user() method - which will perform the session
+storage process again (complete with call to for_session()).
+
+=back
+
+More detailed information about these processes is below.
=head2 INITIALIZATION
instantiates both a new credential object and a new store object. See below
for the details of how credentials and stores are instantiated.
- NOTE: The instances created will remain active throughout the entire
- lifetime of the application, and so should be relatively lightweight.
- Care should be taken to ensure that they do not grow, or retain
- information per request, because they will be involved in each
- authentication request and could therefore substantially
- hurt memory consumption over time.
+B<NOTE>: The instances created will remain active throughout the entire
+lifetime of the application, and so should be relatively lightweight.
+Care should be taken to ensure that they do not grow, or retain
+information per request, because they will be involved in each
+authentication request and could therefore substantially
+hurt memory consumption over time.
=head2 AUTHENTICATION
=over 4
-=item new( $config, $app )
+=item new( $config, $app, $realm )
The C<new()> method is called only once, during the setup process of
L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>. The
information for the store module. The second argument is a reference to the
Catalyst application.
- Note that when new() is called, Catalyst has not yet loaded the various
- controller and model classes, nor is it definite that other plugins have
- been loaded, so your new() method must not rely on any of those being
- present. If any of this is required for your store to function, you should
- defer that part of initialization until the first method call.
+Note that when new() is called, Catalyst has not yet loaded
+the various controller and model classes, nor is it definite
+that other plugins have been loaded, so your new() method
+must not rely on any of those being present. If any of
+this is required for your store to function, you should
+defer that part of initialization until the first method call.
The C<new()> method should return a blessed reference to your store object.
is what $authinfo is required to contain. Many stores will simply use a
username element in $authinfo to locate the user, but more advanced functionality
is possible and you may bend the $authinfo to your needs. Be aware, however, that
-both Credentials and Stores work with the same $authinfo hash, so take care to
-avoid overlapping element names.
+both Credentials and Stores usually work with the same $authinfo hash, so take
+care to avoid overlapping element names.
Please note that this routine may be called numerous times in various
circumstances, and that a successful match for a user here does B<NOT>
underlying user object can do certain things. In most cases you will want to
pass this directly to the underlying user class' C<supports> method. Note that
this is used as a B<class> method against the user class and therefore must
-be able to function without an instantiated user object.
+be able to function without an instantiated user object.
=back
part of the system that the application developer will interact with most. As
such, the API for the user object is very rigid. All user objects B<MUST>
inherit from
-L<Catalyst::Plugin::Authentication::User|Catalyst::Plugin::Authentication::User>.
+L<Catalyst::Authentication::User|Catalyst::Authentication::User>.
=head3 USER METHODS
The routines required by the
L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> plugin
are below. Note that of these, only get_object is strictly required, as the
-L<Catalyst::Plugin::Authentication::User|Catalyst::Plugin::Authentication::User>
+L<Catalyst::Authentication::User|Catalyst::Authentication::User>
base class contains reasonable implementations of the rest. If you do choose
to implement only the C<get_object()> routine, please read the base class code
and documentation so that you fully understand how the other routines will be
C<find_user()> routine as C<id =E<gt> $user-E<gt>id> so you should ensure that your
store's C<find_user()> can cope with that.
-=item supports_features( )
+=item supports( $feature, $subfeature ... )
+
+This method checks to see if the user class supports a particular feature. It
+is implemented such that each argument provides a subfeature of the previous
+argument. In other words, passing 'foo', 'bar' would return true if the user
+supported the 'foo' feature, and the 'bar' feature of 'foo'. This is implemented
+in Catalyst::Authentication::User, so if your class inherits from that, you
+do not need to implement this and can instead implement supported_features().
-This method should return a hashref of 'extra' features supported. This is for
-more flexible integration with some Credentials / applications. It is not
-required that you support anything, and returning C<undef> is perfectly
+B<Note:> If you want the authentication module to be able to save your user in
+the session you must return true when presented with the feature 'session'.
+
+=item supported_features( )
+
+This method should return a hashref of features supported by the user class.
+This is for more flexible integration with some Credentials / applications. It
+is not required that you support anything, and returning C<undef> is perfectly
acceptable and in most cases what you will do.
=item get( $fieldname )
information. This is used as a standard method of accessing an authenticated
user's data, and MUST be implemented by all user objects.
- Note: There is no equivalent 'set' method. Each user class is likely
- to vary greatly in how data must be saved and it is therefore impractical to
- try to provide a standard way of accomplishing it. When an application
- developer needs to save data, they should obtain the underlying object / data
- by calling get_object, and work with it directly.
+B<Note>: There is no equivalent 'set' method. Each user class is
+likely to vary greatly in how data must be saved and it is
+therefore impractical to try to provide a standard way of
+accomplishing it. When an application developer needs to save
+data, they should obtain the underlying object / data by
+calling get_object, and work with it directly.
=item get_object( )
=head1 WRITING A CREDENTIAL
-... Documentation fairy fell asleep here.
+Compared to writing a store, writing a credential is very simple. There is only
+one class to implement, and it consists of only two required routines. They are:
+
+ new() - instantiates the credential object
+ authenticate() - performs the authentication and returns a user object
+
+=head2 CREDENTIAL METHODS
+
+=over 4
+
+=item new( $config, $app, $realm )
+
+Like the Store method of the same name, the C<new()> method is called only
+once, during the setup process of
+L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>. The
+first argument, C<$config>, is a hash reference containing the configuration
+information for the credential module. The second argument is a reference
+to the Catalyst application. $realm is the instantiated Realm object, which
+you may use to access realm routines - such as find_user.
+
+Again, when the credential's new() method is called, Catalyst
+has not yet loaded the various controller and model classes.
+
+The new method should perform any necessary setup required and instantiate
+your credential object. It should return your instantiated credential.
+
+=item authenticate( $c, $realm, $authinfo )
+
+This is the workhorse of your credential. When $c->authenticate() is called
+the L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> module retrieves the
+realm object and passes it, along with the $authinfo hash
+to your credential's authenticate method. Your module should use the
+$authinfo hash to obtain the user from the realm passed, and then perform
+any credential verification steps necessary to authenticate the user. This
+method should return the user object returned by the authentication store if
+credential verification succeeded. It should return undef on failure.
+
+How your credential module performs the credential verification is entirely
+up to you. In most cases, the credential will retrieve a user from the store
+first (using the stores find_user() method), and then validate the user's
+information. However, this does not have to be the case.
+
+It is perfectly acceptable for your credential to perform other tasks prior to
+attempting to retrieve the user from the store. It may also make sense for
+your credential to perform activities which help to locate the user in
+question, for example, finding a user id based on an encrypted token.
+In these scenarios, the $authinfo hash passed to find_user()
+can be different than that which is passed in to $c->authenticate(). Once
+again this is perfectly acceptable if it makes sense for your credential,
+though you are strongly advised to note this behavior clearly in your
+credential's documentation - as application authors are almost
+certainly expecting the user to be found using the information provided
+to $c->authenticate().
+
+Look at the L<Catalyst::Authentication::Credential::Password|Catalyst::Authentication::Credential::Password>
+module source to see this in action. In order to avoid possible
+mismatches between the encrypted and unencrypted passwords, the password
+credential actually removes the provided password from the authinfo
+array. It does this because, in many cases, the store's password
+field will be encrypted in some way, and the password passed to
+$c->authenticate is almost certainly in plaintext.
+
+NOTE: You should always assume that a store is going to use all
+the information passed to it to locate the user in question.
+If there are fields in the $authinfo hash that you are sure
+are specific to your credential, you may want to consider
+removing them before user retrieval. A better solution is to
+place those arguments that are specific to your credential
+within their own subhash named after your module.
+
+The L<Catalyst::Authentication::Store::DBIx::Class|Catalyst::Authentication::Store::DBIx::Class> module does this
+in order to encapsulate arguments intended specifically for
+that module. See the L<Catalyst::Authentication::Store::DBIx::Class::User|Catalyst::Authentication::Store::DBIx::Class::User>
+source for details.
+
+=back
+
+=head1 AUTHORS
+
+Jay Kuri, C<jayk@cpan.org>
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright (c) 2005 the aforementioned authors. All rights
+reserved. This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+=cut
+