=head1 NAME Catalyst::Plugin::Authentication::Internals - All about authentication Stores and Credentials =head1 INTRODUCTION L provides a standard authentication interface to application developers using the Catalyst framework. It is designed to allow application developers to use various methods of user storage and credential verification. It is also designed to provide for minimal change to the application when switching between different storage and credential verification methods. While L 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 and storage backends, or B, respectively. For authentication to function there must be at least one credential and one storage backend. A pairing of a store and a credential is referred to as a B. There may be any number of realms defined for an application, though most applications will not require more than one or two. The details of using this module can be found in the L documentation. What follows is an explanation of how the module functions internally and what is required to implement a credential or a store. =head1 OVERVIEW 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. =head2 INITIALIZATION When the authentication module is loaded, it reads it's configuration to determine the realms to set up for the application and which realm is to be the default. For each realm defined in the application's config, L 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. =head2 AUTHENTICATION When C<$c-Eauthenticate()> is called from within an application, the objects created in the initialization process come into play. C<$c-Eauthenticate()> takes two arguments. The first is a hash reference containing all the information available about the user. This will be used to locate the user in the store and verify the user's credentials. The second argument is the realm to authenticate against. If the second argument is omitted, the default realm is assumed. The main authentication module then locates the credential and store objects for the realm specified and calls the credential object's C method. It provides three arguments, first the application object, or C<$c>, then a reference to the store object, and finally the hashref provided in the C<$c-Eauthenticate> call. The main authentication module expects the return value to be a reference to a user object upon successful authentication. If it receives anything aside from a reference, it is considered to be an authentication failure. Upon success, the returned user is marked as authenticated and the application can act accordingly, using C<$c-Euser> to access the authenticated user, etc. Astute readers will note that the main L module does not interact with the store in any way, save for passing a reference to it to the credential. This is correct. The credential object is responsible for obtaining the user from the provided store using information from the userinfo hashref and/or data obtained during the credential verification process. =head1 WRITING A STORE There are two parts to an authentication store, the backend and the user object. =head2 STORAGE BACKEND Writing a storage backend is actually quite simple. There are only five methods that must be implemented. They are: new() - instantiates the store object find_user() - locates a user using data contained in the hashref for_session() - prepares a user to be stored in the session from_session() - does any restoration required when obtaining a user from the session user_supports() - provides information about what the user object supports =head3 STORE METHODS =over 4 =item new( $config, $app ) The C method is called only once, during the setup process of L. The 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. The C method should return a blessed reference to your store object. =item find_user( $authinfo, $c ) This is the workhorse of any authentication store. It's job is to take the information provided to it via the C<$authinfo> hashref and locate the user that matches it. It should return a reference to a user object. A return value of anything else is considered to mean no user was found that matched the information provided. How C 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. Please note that this routine may be called numerous times in various circumstances, and that a successful match for a user here does B necessarily constitute successful authentication. Your store class should never assume this and in most cases C<$c> B by your store object. =item for_session( $c, $user ) This method is responsible for preparing a user object for storage in the session. It should return information that can be placed in the session and later used to restore a user object (using the C method). It should therefore ensure that whatever information provided can be used by the C method to locate the unique user being saved. Note that there is no guarantee that the same Catalyst instance will receive both the C and C calls. You should take care to provide information that can be used to restore a user, regardless of the current state of the application. A good rule of thumb is that if C can revive the user with the given information even if the Catalyst application has just started up, you are in good shape. =item from_session( $c, $frozenuser ) This method is called whenever a user is being restored from the session. C<$frozenuser> contains the information that was stored in the session for the user. This will under normal circumstances be the exact data your store returned from the previous call to C. C should return a valid user object. =item user_supports( $feature, ... ) This method allows credentials and other objects to inquire as to what the underlying user object is capable of. This is pretty-well free-form and the main purpose is to allow graceful integration with credentials and 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 method. Note that this is used as a B method against the user class and therefore must be able to function without an instantiated user object. =back =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 inherit from L. =head3 USER METHODS The routines required by the L plugin are below. Note that of these, only get_object is strictly required, as the L base class contains reasonable implementations of the rest. If you do choose to implement only the C routine, please read the base class 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 to provide the functionality you need. So long as the below are implemented, and you don't overlap the base class' methods with incompatible routines, you should experience no problems. =over 4 =item id( ) The C method should return a unique id (scalar) that can be used to retreive this user from the store. Often this will be provided to the store's C routine as C $user-Eid> so you should ensure that your store's C can cope with that. =item supports_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 is perfectly acceptable and in most cases what you will do. =item get( $fieldname ) This method should return the value of the field matching fieldname provided, or undef if there is no field matching that fieldname. In most cases this will 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. =item get_object( ) This method returns the underlying user object. If your user object is backed by another object class, this method should return that underlying object. This allows the application developer to obtain an editable object. Generally speaking this will only be done by developers who know what they are doing and require advanced functionality which is either unforeseen or inconsistent across user classes. If your object is not backed by another class, or you need to provide additional intermediate functionality, it is perfectly 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. When you need multiple authentication backends per application then you must fetch things yourself. For example: 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. This method should accept an arbitrary list of parameters (determined by you or the credential verifyer), and return an object inheriting L. For introspection purposes you can also define the C 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 method. When the user object is saved in the session the C 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). =head2 Optional Features Each user has the C 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 Sometimes a store is agnostic to the credentials (DB storage, for example), but sometimes it isn't (like an Htpasswd file). If you are writing a backend that wraps around a module, like L wraps around L, it makes sense to delegate the credential checks. This particular example caused the following "feature" to be added: $user->supports(qw/password self_check/); =head2 Writing a plugin to go with the backend Typically the backend will do the heavy lifting, by registering a store. These plugins should look something like this: sub setup { my $c = shift; $c->default_auth_store( # a store can be an object or a class Catalyst::Plugin::Authentication::Store::Foo::Backend->new( ... ) ); $c->NEXT::setup(@_); }