Get headings right, castaway++
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Plugin / Authentication / Internals.pod
index 61688d9..9649cd8 100644 (file)
@@ -17,7 +17,7 @@ provides the interface to the application developer, the actual work of
 verifying the credentials and retrieving users is delegated to separate
 modules. These modules are called B<Credentials> and storage backends, or
 B<Stores>, respectively. For authentication to function there must be at least
-one credential and one storage backend. A pairing of a store and a credential
+one credential and one store. A pairing of a store and a credential
 is referred to as a B<Realm>. There may be any number of realms defined for an
 application, though most applications will not require more than one or two.
 
@@ -32,7 +32,102 @@ is required to implement a credential or a store.
 
 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
 
@@ -43,12 +138,12 @@ L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>
 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
 
@@ -81,11 +176,11 @@ process.
 
 =head1 WRITING A STORE
 
-There are two parts to an authentication store, the backend and the user object.
+There are two parts to an authentication store, the store object and the user object.
 
 =head2 STORAGE BACKEND
 
-Writing a storage backend is actually quite simple.  There are only five methods
+Writing a store is actually quite simple.  There are only five methods
 that must be implemented. They are:
 
     new()           - instantiates the store object
@@ -98,7 +193,7 @@ that must be implemented. They are:
 
 =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
@@ -106,12 +201,13 @@ first argument, C<$config>, is a hash reference containing the configuration
 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.
 
 =item find_user( $authinfo, $c ) 
@@ -126,8 +222,8 @@ How C<find_user()> accomplishes it's job is entirely up to you, the author, as
 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>
@@ -166,27 +262,40 @@ applications that may provide advanced functionality based on whether the
 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
 
+=head3 OPTIONAL STORE METHODS
+
+If you want your store to be able to auto- create users, then you can
+implement these methods:
+
+=head4 auto_update_user( $authinfo, $c, $res )
+
+This method is called if the realm's auto_update_user setting is true.
+
+=head4 auto_create_user( $authinfo, $c )
+
+This method is called if the realm's auto_create_user setting is true.
+
 =head2 USER OBJECT
 
 The user object is an important piece of your store module. It will be the
 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
-documentation so that you fully understand how the other routines will be
+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
 implemented for you.
 
 Also, your user object can implement whatever additional methods you require
@@ -203,11 +312,23 @@ retreive this user from the store.  Often this will be provided to the store's
 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 )
@@ -218,11 +339,12 @@ access the underlying storage mechanism for the user data and return the
 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( )
@@ -239,126 +361,94 @@ reasonable to return C<$self>.
 =back 
 
 
-... Documentation fairy fell asleep here.  
-
-
-=head1 MULTIPLE BACKENDS
-
-A key issue to understand about authentication stores is that there are
-potentially many of them. Each one is registered into the application, and has
-a name.
-
-For most applications, there is only one, and in this framework it is called
-'default'.
-
-When you use a plugin, like
-
-    use Catalyst qw/
-        Authentication
-        Authentication::Store::Foo
-    /;
-
-the Store plugins typically only act at setup time. They rarely do more than
-check out the configuration, and register e.g. Store::Foo::Backend, and set it
-as the default store.
-
-    __PACKAGE__->default_auth_store( $store );
-
-    # the same as
-
-    __PACKAGE__->register_auth_stores( default => $store );
-
-=head1 WORKING WITH USERS
-
-All credential verifiers should accept either a user object, or a user ID.
-
-If a user ID is provided, then they will fetch the user object from the default
-store, and check against it.
-
-This should be pretty much DWIM all the time.
+=head1 WRITING A CREDENTIAL
 
-When you need multiple authentication backends per application then you must
-fetch things yourself. For example:
+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:
 
-    my $user = $c->get_auth_store("other_store")->get_user($id);
-
-    $c->login( $user, $supplied_password );
-
-Instead of just:
-
-    $c->login( $id, $supplied_password );
-
-which will go to the default store.
-
-=head1 WRITING A BACKEND
-
-Writing an authentication storage backend is a very simple matter.
-
-The only method you really need to support is C<get_user>.
-
-This method should accept an arbitrary list of parameters (determined by you or
-the credential verifyer), and return an object inheriting
-L<Catalyst::Plugin::Authentication::User>.
-
-For introspection purposes you can also define the C<user_supports> method. See
-below for optional features. This is not necessary, but might be in the future.
-
-=head2 Integrating with Catalyst::Plugin::Session
-
-If your users support sessions, your store should also define the
-C<from_session> method. When the user object is saved in the session the
-C<for_session> method is called, and that is used as the value in the session
-(typically a user id). The store is also saved in the hash. If
-C<<$user->store>> returns something registered, that store's name is used. If
-not, the user's class is used as if it were a store (and must also support
-C<from_session>).
-
-=head2 Optional Features
-
-Each user has the C<supports> method. For example:
-
-    $user->supports(qw/password clear/);
-
-should return a true value if this specific user has a clear text password.
-
-This is on a per user (not necessarily a per store) basis. To make assumptions
-about the store as a whole,
-
-    $store->user_supports(qw/password clear/);
-
-is supposed to be the lowest common denominator.
-
-The standardization of these values is to be goverened by the community,
-typically defined by the credential verification plugins.
-
-=head2 Stores implying certain credentials
+    new()           - instantiates the credential object
+    authenticate()  - performs the authentication and returns a user object
+    
+=head2 CREDENTIAL METHODS
 
-Sometimes a store is agnostic to the credentials (DB storage, for example), but
-sometimes it isn't (like an Htpasswd file).
+=over 4
 
-If you are writing a backend that wraps around a module, like
-L<Catalyst::Plugin::Authentication::Store::Htpasswd> wraps around
-L<Authen::Htpasswd>, it makes sense to delegate the credential checks.
+=item new( $config, $app, $realm )
 
-This particular example caused the following "feature" to be added:
+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.
 
-    $user->supports(qw/password self_check/);
+=back
 
-=head2 Writing a plugin to go with the backend
+=head1 AUTHORS
 
-Typically the backend will do the heavy lifting, by registering a store.
+Jay Kuri, C<jayk@cpan.org>
 
-These plugins should look something like this:
+=head1 COPYRIGHT & LICENSE
 
-    sub setup {
-        my $c = shift;
+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.
 
-        $c->default_auth_store(
-            # a store can be an object or a class
-            Catalyst::Plugin::Authentication::Store::Foo::Backend->new(
-                ...
-            )
-        );
+=cut
 
-        $c->NEXT::setup(@_);
-    }