Fix incorrect method name, thanks cmilfo!
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Plugin / Authentication.pm
index fc7388f..8dd1a19 100644 (file)
@@ -228,12 +228,223 @@ the user. Multiple stores can be accessed from within one application.
 Credentials are used to verify users, using the store, given data from 
 the frontend.
 
-To implement authentication in a catalyst application you need to add this 
+To implement authentication in a Catalyst application you need to add this 
 module, plus at least one store and one credential module.
 
 Authentication data can also be stored in a session, if the application 
 is using the L<Catalyst::Plugin::Session> module.
 
+=head1 INTRODUCTION
+
+=head2 The Authentication/Authorization Process
+
+Web applications typically need to identify a user - to tell the user apart
+from other users. This is usually done in order to display private information
+that is only that user's business, or to limit access to the application so
+that only certain entities can access certain parts.
+
+This process is split up into several steps. First you ask the user to identify
+themselves. At this point you can't be sure that the user is really who they
+claim to be.
+
+Then the user tells you who they are, and backs this claim with some piece of
+information that only the real user could give you. For example, a password is
+a secret that is known to both the user and you. When the user tells you this
+password you can assume they're in on the secret and can be trusted (ignore
+identity theft for now). Checking the password, or any other proof is called
+B<credential verification>.
+
+By this time you know exactly who the user is - the user's identity is
+B<authenticated>. This is where this module's job stops, and other plugins step
+in. The next logical step is B<authorization>, the process of deciding what a
+user is (or isn't) allowed to do. For example, say your users are split into
+two main groups - regular users and administrators. You should verify that the
+currently logged in user is indeed an administrator before performing the
+actions of an administrative part of your application. One way to do this is
+with role based access control.
+
+=head2 The Components In This Framework
+
+=head3 Credential Verifiers
+
+When user input is transferred to the L<Catalyst> application (typically via
+form inputs) this data then enters the authentication framework through these
+plugins.
+
+These plugins check the data, and ensure that it really proves the user is who
+they claim to be.
+
+=head3 Storage Backends
+
+The credentials also identify a user, and this family of modules is supposed to
+take this identification data and return a standardized object oriented
+representation of users.
+
+When a user is retrieved from a store it is not necessarily authenticated.
+Credential verifiers can either accept a user object, or fetch the object
+themselves from the default store.
+
+=head3 The Core Plugin
+
+This plugin on its own is the glue, providing store registration, session
+integration, and other goodness for the other plugins.
+
+=head3 Other Plugins
+
+More layers of plugins can be stacked on top of the authentication code. For
+example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
+browser sessions that is more persistent per users.
+L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
+and group users into categories, and then check which categories the current
+user belongs to.
+
+=head1 EXAMPLE
+
+Let's say we were storing users in an Apache style htpasswd file. Users are
+stored in that file, with a hashed password and some extra comments. Users are
+verified by supplying a password which is matched with the file.
+
+This means that our application will begin like this:
+
+    package MyApp;
+
+    use Catalyst qw/
+        Authentication
+        Authentication::Credential::Password
+        Authentication::Store::Htpasswd
+    /;
+
+    __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
+
+This loads the appropriate methods and also sets the htpasswd store as the
+default store.
+    
+So, now that we have the code loaded we need to get data from the user into the
+credential verifier.
+
+Let's create an authentication controller:
+
+    package MyApp::Controller::Auth;
+
+    sub login : Local {
+        my ( $self, $c ) = @_;
+
+        if (    my $user = $c->req->param("user")
+            and my $password = $c->req->param("password") )
+        {
+            if ( $c->login( $user, $password ) ) {
+                $c->res->body( "hello " . $c->user->name );
+            } else {
+                # login incorrect
+            }
+        }
+        else {
+            # invalid form input
+        }
+    }
+
+This code should be very readable. If all the necessary fields are supplied,
+call the L<Authentication::Credential::Password/login> method on the
+controller. If that succeeds the user is logged in.
+
+It could be simplified though:
+
+    sub login : Local {
+        my ( $self, $c ) = @_;
+
+        if ( $c->login ) {
+            ...
+        }
+    }
+
+Since the C<login> method knows how to find logically named parameters on it's
+own.
+
+The credential verifier will ask the default store to get the user whose ID is
+the user parameter. In this case the default store is the htpasswd one. Once it
+fetches the user from the store the password is checked and if it's OK
+C<< $c->user >> will contain the user object returned from the htpasswd store.
+
+We can also pass a user object to the credential verifier manually, if we have
+several stores per app. This is discussed in
+L<Catalyst::Plugin::Authentication::Store>.
+
+Now imagine each admin user has a comment set in the htpasswd file saying
+"admin".
+
+A restricted action might look like this:
+
+    sub restricted : Local {
+        my ( $self, $c ) = @_;
+
+        $c->detach("unauthorized")
+          unless $c->user_exists
+          and $c->user->extra_info() eq "admin";
+
+        # do something restricted here
+    }
+
+This is somewhat similar to role based access control.
+L<Catalyst::Plugin::Authentication::Store::Htpasswd> treats the extra info
+field as a comma separated list of roles if it's treated that way. Let's
+leverage this. Add the role authorization plugin:
+
+    use Catalyst qw/
+        ...
+        Authorization::Roles
+    /;
+
+    sub restricted : Local {
+        my ( $self, $c ) = @_;
+
+        $c->detach("unauthorized") unless $c->check_user_roles("admin");
+
+        # do something restricted here
+    }
+
+This is somewhat simpler and will work if you change your store, too, since the
+role interface is consistent.
+
+Let's say your app grew, and you now have 10000 users. It's no longer efficient
+to maintain an htpasswd file, so you move this data to a database.
+
+    use Catalyst qw/
+        Authentication
+        Authentication::Credential::Password
+        Authentication::Store::DBIC
+        Authorization::Roles
+    /;
+
+    __PACKAGE__->config->{authentication}{dbic} = ...; # see the DBIC store docs
+
+The rest of your code should be unchanged. Now let's say you are integrating
+typekey authentication to your system. For simplicity's sake we'll assume that
+the user's are still keyed in the same way.
+
+    use Catalyst qw/
+        Authentication
+        Authentication::Credential::Password
+        Authentication::Credential::TypeKey
+        Authentication::Store::DBIC
+        Authorization::Roles
+    /;
+
+And in your auth controller add a new action:
+
+    sub typekey : Local {
+        my ( $self, $c ) = @_;
+
+        if ( $c->authenticate_typekey) { # uses $c->req and Authen::TypeKey
+            # same stuff as the $c->login method
+            # ...
+        }
+    }
+
+You've now added a new credential verification mechanizm orthogonally to the
+other components. All you have to do is make sure that the credential verifiers
+pass on the same types of parameters to the store in order to retrieve user
+objects.
+
 =head1 METHODS
 
 =over 4 
@@ -251,14 +462,14 @@ user from the auth store.
 
 If you're just going to say
 
-       if ( $c->user_user ) {
+       if ( $c->user_exists ) {
                # foo
        } else {
                $c->forward("login");
        }
 
-it should be more efficient than C<< $c->user >> when a user is marked in the session
-but C<< $c->user >> hasn't been called yet.
+it should be more efficient than C<< $c->user >> when a user is marked in the
+session but C<< $c->user >> hasn't been called yet.
 
 =item logout
 
@@ -375,22 +586,45 @@ Sets the default configuration parameters.
 
 =head1 SEE ALSO
 
-L<Catalyst::Plugin::Authentication::Credential::Password>,
+This list might not be up to date.
+
+=head2 User Storage Backends
+
 L<Catalyst::Plugin::Authentication::Store::Minimal>,
+L<Catalyst::Plugin::Authentication::Store::Htpasswd>,
+L<Catalyst::Plugin::Authentication::Store::DBIC> (also works with Class::DBI).
+
+=head2 Credential verification
+
+L<Catalyst::Plugin::Authentication::Credential::Password>,
+L<Catalyst::Plugin::Authentication::Credential::HTTP>,
+L<Catalyst::Plugin::Authentication::Credential::TypeKey>
+
+=head2 Authorization
+
 L<Catalyst::Plugin::Authorization::ACL>,
-L<Catalyst::Plugin::Authorization::Roles>.
+L<Catalyst::Plugin::Authorization::Roles>
+
+=head2 Internals Documentation
+
+L<Catalyst::Plugin::Authentication::Store>
+
+=head2 Misc
+
+L<Catalyst::Plugin::Session>,
+L<Catalyst::Plugin::Session::PerUser>
 
 =head1 DON'T SEE ALSO
 
-This module along with it's sub plugins deprecate a great number of other
-modules. These include Catalyst::Plugin::Authentication::Simple,
-Catalyst::Plugin::Authentication::CDBI.
+This module along with its sub plugins deprecate a great number of other
+modules. These include L<Catalyst::Plugin::Authentication::Simple>,
+L<Catalyst::Plugin::Authentication::CDBI>.
 
 At the time of writing these plugins have not yet been replaced or updated, but
-should be eventually: Catalyst::Plugin::Authentication::OpenID,
-Catalyst::Plugin::Authentication::LDAP,
-Catalyst::Plugin::Authentication::CDBI::Basic,
-Catalyst::Plugin::Authentication::Basic::Remote
+should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
+L<Catalyst::Plugin::Authentication::LDAP>,
+L<Catalyst::Plugin::Authentication::CDBI::Basic>,
+L<Catalyst::Plugin::Authentication::Basic::Remote>.
 
 =head1 AUTHORS