Create, then Authenticate as User in Catalyst

A client project stymied me for a little bit this week. One facet of the application revolves around user account creation and authentication. I'm using Catalyst for the web side and DBIC for the database side.

For now, I want to be able to create a user account, sanitize the information, and then immediate log in the user and redirect to another URI which requires user authentication. I use CatalystX::SimpleLogin to provide login/logout actions, as well as the DBIC password and session cookie options for Catalyst::Plugin::Authentication. With minimal changes to my database as originally planned, user passwords get hashed with Digest::SHA1 for an additional layer of paranoia.

CatalystX::SimpleLogin provides chained actions which will enforce login/logout to protect other actions which require authentication. I have a two base actions in the User controller which perform setup of the relevant Resultset, among other things:

sub base :Chained('/') :PathPart('users') :CaptureArgs(0)
    my ( $self, $c ) = @_;
    $c->stash( users_rs => $c->model( 'DB::User' ) );

sub authbase :Chained('/login/required') :PathPart('users') :CaptureArgs(0)
    my ( $self, $c ) = @_;
    $c->stash( users_rs => $c->model( 'DB::User' ) );

The action to create a user chains off of base, so it does not require authentication:

sub add :Chained('base') :PathPart('add') :Args(0)

After validating various parameters, it finally attempts to create the new user in the database:

    my $newuser = try
        $users_rs->create( \%user_params )
            user   => \%user_params,
            errors => $_,

    return unless $newuser;

At this point, $newuser represents the new user. Here comes the fun part:

    # remove any credentials and force new authentication
        username => $params->{username},
        password => $params->{password},

If there's an existing authorized user session, this code invalidates it, then authenticates with the new user's data. (An earlier version of this code used user data from $newuser itself, but I couldn't explain to myself why the SHA-1 password from $newuser->password should work.) There shouldn't be an existing user session, but $c->logout() is a cheap way to avoid trouble.

    # force simple auth to reauthenticate

If there's an existing session, the id should change to reflect the new authentication properties. Again, this is a cheap way to avoid trouble. The really interesting part of the code is the second line, which performs the login action, using the current-but-updated credentials, and immediately returns to the current action. This makes CatalystX::SimpleLogin do the right thing on the next request without the user knowing anything about a login form.

    # and display the profile page
    $c->res->redirect( $c->uri_for( 'profile', $newuser->id ) );

To avoid the double-POST problem, this action finally redirects to the "Hooray, you've created a new user!" action. This simple HTTP redirect ends up in the profile action:

sub profile :Chained('authbase') :Path :Args(1)

Because of the chain to authbase, anyone who visits the /usrs/profile/id URI must log in, whether manually through the CatalystX::SimpleLogin form or automatically after having created a new account.

At some point in the future I may need to change the application to perform email validation or admin approval of new accounts, but for now it seemed easiest to assume that users who create new accounts want to use those new accounts automatically.

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide



About this Entry

This page contains a single entry by chromatic published on December 16, 2010 10:33 AM.

Modern JavaScript was the previous entry in this blog.

Counting Modules is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Powered by the Perl programming language

what is programming?