Adding Authentication to your Catalyst App

Catalyst has one of the most flexible built-in authentication systems available in a web-framework. Largely because of this flexibility, the first glance at the documentation can be somewhat intimidating. For the average application, however, the Catalyst auth system is quite simple. Today we are going to have a quick crash-course in basic Catalyst Authentication.

The Players

For authentication to function in Catalyst, you need two things, a Store module and a Credential module.

Put simply, a Store is the place where your user information is stored. Most commonly this is an SQL database, though there are many other possibilities.

A Credential module verifies the identity of the user. Usually, it does this by comparing the information provided by the user to the information found in the Store.

For the average Catalyst application, the modules that are used are the Password credential and the DBIx::Class store.

What about realms?

If you've looked at the documentation for Catalyst Authentication, you will have run across the term 'Realm.' Realms are a relatively new feature that allows great flexibility in the way your Catalyst application performs authentication. Put simply, realms allow you to pair a Credential and a Store together. Realms allow a Catalyst application to have multiple methods for authenticating users.

That said, in the vast majority of applications, there will only be one realm, the 'default' realm. In these single-realm applications you can effectively ignore the realms functionality altogether. The only realm related anything you'll have to deal with will be the single appearance of the word 'realms' in the configuration file.

Preparation

In order to use the Authentication system for Catalyst, you must first install the Authentication Plugin. This module Catalyst::Plugin::Authentication is available at your favorite CPAN repository.

NOTE: This article assumes you are using the most recent version available (as of this writing 0.10003**).

The Password credential module is part of the main Authentication plugin, so there is no need to install that separately.

The DBIx::Class store is available as

Catalyst::Authentication::Store::DBIx::Class

In most applications, you will also want to use sessions in order to allow your users to remain logged in across page loads. You will need the following modules:

 Catalyst::Plugin::Session
 Catalyst::Plugin::Session::State::Cookie
 Catalyst::Plugin::Session::Store::FastMmap

Fortunately, for the average installation, little if any configuration is required for these modules to function. Simply loading them into your application is enough.

So once all of these modules are installed, you can proceed to ...

Configuration

To enable authentication in your application, there are only two things you need to do. First, you must add the Authentication and session modules to the use Catalyst section of your application

 use Catalyst qw/ ...
                 Authentication
                 Session
 				 Session::Store::FastMmap
 				 Session::State::Cookie
 				 ... 
 				/;

This turns on the authentication and session modules. The next step is to configure the auth modules to match your application. A complete configuration would look like this (assuming you are using the YAML config type)

 authentication:
   realms:
     default:
       credential:
         class: Password
         password_field: password
         password_type: clear
       store:
         class: DBIx::Class
         user_class: MyApp::Users




This sets up a simple authentication system for your application. This configuration indicates that you will be using the 'Password' credential checking module, and that the password is stored 'clear' or in plaintext in your database.

This configuration also indicates that your user information should be retrieved using the DBIx::Class storage module, which interfaces with your application's DBIx::Class schema to retrieve user information. The user_class configuration element tells the store which schema class to use in order to find a user. In this case, the actual database table is probably called 'users', if your user table is 'members', than this would likely be MyApp::Members.

The action

If you have completed the above steps, your application is up and running with Authentication enabled. The application probably isn't behaving any differently though. That's because we've only made your app capable of handling authentication. Now we need to actually use the authentication system.

There are three parts to using basic authentication in your application:

1 Authenticating your user - a.k.a. 'logging in'
2 Checking that a valid user has logged in
3 De-authorizing - a.k.a. 'logging out'

We will look at each of these steps below. First...

Authenticating your user

To authenticate your user, you need to call the $c->authenticate() routine from some Catalyst action. Usually this is done in a login action such as:

 sub login : Local {
     my ( $self, $c ) = @_;

     if ($c->authenticate( { 
                              username => $c->request->params->{'username'}, 
                              password => $c->request->params->{'password'} 
                           } )) {   
        # $c->authenticate returns a true value if authentication succeeds
        # so display the login successful page here.
     } else {
        # or undef is authentication failed.  
        # so display the 'try again' page here.
     }
 }

The above routine checks the username and password via the authentication system and responds based on whether authentication succeeded or not. If authentication succeeds the user information is stored in the session and you can proceed on to step two.

Checking that a valid user has logged in

Now that you have a login method in place and your users can actually log in, it's time to add the code that behaves differently based on whether there is a logged in user or not. A common example of this might be a user preference page.

Since we can only save preferences for an existing user, if someone attempts to use the preference page who has not logged in, we want to bounce them to the login screen. We can check to see if a user is logged in by using the $c->user_exists() method. As you might expect, $c->user_exists() returns true if a user has logged in and false otherwise. An example of how you might use this follows:

 sub preferences : Local {
     my ( $self, $c ) = @_;

     if ( $c->user_exists() ) {

         ## If a user is logged in - you can show them the preferences page.
         $c->stash->{'template'} = 'preferences.tt2';

     } else {

         # otherwise bounce them to the login page.
         $c->response->redirect($c->uri_for('login'));

     }
 }

Note that you will also want to check $c->user_exists() on your preference save action also.

De-authorizing - a.k.a. 'logging out'

You are almost finished adding authentication to your application. The only thing left that you need to do is to allow users to log-out. This is probably the easiest part of the authentication system to use. To de-authorize a user, you simply call $c->logout(). $c->logout() will have no ill effects even if there is no user currently logged in, so your logout action is as simple as it gets:

 sub logout : Local {
    my ( $self, $c ) = @_;

    $c->logout();
 }

Congratulations! After adding the logout routine above - your application now has a functional authentication system.

A more advanced DBIx::Class authentication routine

The above demonstrates just about the simplest example of authentication. In many cases, however, the simplest usage is all that is necessary.

As we mentioned, though, the authentication system is capable of much more than what you see above. For example, you can use more than just username and password to authenticate. It is not uncommon for a user to have a status associated with it, such as 'registered', 'active' or 'disabled.' In most cases, you don't want disabled users to be able to log in.

You could authenticate the user, and then check their status, but there is an easier way. Let's make status part of the authenticate check. This is simple, as the DBIx::Class store uses any field you pass to $c->authenticate() to find a matching user. An example will clarify this, I think, let's revisit our login action:

 sub login : Local {
     my ( $self, $c ) = @_;

     if ($c->authenticate( { 
                              username => $c->request->params->{'username'}, 
                              password => $c->request->params->{'password'},
                              status => [ 'registered', 'active' ]
                           } )) {   
        # display the login successful page here.
     } else {
        # display the 'try again' page here.
     }
 }

Using the above action, only users who have a status of 'registered' or 'active' will be able to log in.

Further reading

This is just a hint at what the Catalyst Authentication system is capable of. For more information, take a look at the Catalyst::Plugin::Authentication documentation:

https://metacpan.org/module/Catalyst::Plugin::Authentication

And for more advanced DBIx::Class authentication options - check out the DBIx::Class store docs:

https://metacpan.org/module/Catalyst::Authentication::Store::DBIx::Class

AUTHOR

jayk - Jay Kuri <jayk@cpan.org>