X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FAuthentication.pod;h=c8b21a876cd9bee3e00ff44032cc3eba67ed7faf;hp=499aa5346472935ed63cae375ff2be9b553b51a8;hb=56a2fe2a23306819678c534f48c4932866ec825e;hpb=94d8da411a980c822af29da44d4cbf62a72c25c1 diff --git a/lib/Catalyst/Manual/Tutorial/Authentication.pod b/lib/Catalyst/Manual/Tutorial/Authentication.pod index 499aa53..c8b21a8 100644 --- a/lib/Catalyst/Manual/Tutorial/Authentication.pod +++ b/lib/Catalyst/Manual/Tutorial/Authentication.pod @@ -1,11 +1,11 @@ =head1 NAME -Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 5: Authentication +Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Chapter 5: Authentication =head1 OVERVIEW -This is B for the Catalyst tutorial. +This is B for the Catalyst tutorial. L @@ -58,15 +58,16 @@ L Now that we finally have a simple yet functional application, we can focus on providing authentication (with authorization coming next in -Part 6). +Chapter 6). -This part of the tutorial is divided into two main sections: 1) basic, +This chapter of the tutorial is divided into two main sections: 1) basic, cleartext authentication and 2) hash-based authentication. You can checkout the source code for this example from the catalyst subversion repository as per the instructions in L. + =head1 BASIC AUTHENTICATION This section explores how to add authentication logic to a Catalyst @@ -77,7 +78,7 @@ application. First, we add both user and role information to the database (we will add the role information here although it will not be used until the -authorization section, Part 6). Create a new SQL script file by opening +authorization section, Chapter 6). Create a new SQL script file by opening C in your editor and insert: -- @@ -125,18 +126,19 @@ Although we could manually edit the DBIC schema information to include the new tables added in the previous step, let's use the C option on the DBIC model helper to do most of the work for us: - $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema create=static dbi:SQLite:myapp.db + $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ + create=static components=TimeStamp dbi:SQLite:myapp.db exists "/root/dev/MyApp/script/../lib/MyApp/Model" exists "/root/dev/MyApp/script/../t" Dumping manual schema for MyApp::Schema to directory /root/dev/MyApp/script/../lib ... Schema dump completed. exists "/root/dev/MyApp/script/../lib/MyApp/Model/DB.pm" $ - $ ls lib/MyApp/Schema + $ ls lib/MyApp/Schema/Result Authors.pm BookAuthors.pm Books.pm Roles.pm UserRoles.pm Users.pm Notice how the helper has added three new table-specific result source -files to the C directory. And, more +files to the C directory. And, more importantly, even if there were changes to the existing result source files, those changes would have only been written above the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and your hand-edited @@ -147,7 +149,7 @@ relationship information to the three new result source files. Edit each of these files and add the following information between the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing C<1;>: -C: +C: # # Set relationships: @@ -157,8 +159,8 @@ C: # args: # 1) Name of relationship, DBIC will create accessor with this name # 2) Name of the model class referenced by this relationship - # 3) Column name in *foreign* table - __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::UserRoles', 'user_id'); + # 3) Column name in *foreign* table (aka, foreign key in peer table) + __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::Result::UserRoles', 'user_id'); # many_to_many(): # args: @@ -169,7 +171,7 @@ C: __PACKAGE__->many_to_many(roles => 'map_user_role', 'role'); -C: +C: # # Set relationships: @@ -179,11 +181,11 @@ C: # args: # 1) Name of relationship, DBIC will create accessor with this name # 2) Name of the model class referenced by this relationship - # 3) Column name in *foreign* table - __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::UserRoles', 'role_id'); + # 3) Column name in *foreign* table (aka, foreign key in peer table) + __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::Result::UserRoles', 'role_id'); -C: +C: # # Set relationships: @@ -194,25 +196,25 @@ C: # 1) Name of relationship, DBIC will create accessor with this name # 2) Name of the model class referenced by this relationship # 3) Column name in *this* table - __PACKAGE__->belongs_to(user => 'MyApp::Schema::Users', 'user_id'); + __PACKAGE__->belongs_to(user => 'MyApp::Schema::Result::Users', 'user_id'); # belongs_to(): # args: # 1) Name of relationship, DBIC will create accessor with this name # 2) Name of the model class referenced by this relationship # 3) Column name in *this* table - __PACKAGE__->belongs_to(role => 'MyApp::Schema::Roles', 'role_id'); + __PACKAGE__->belongs_to(role => 'MyApp::Schema::Result::Roles', 'role_id'); The code for these three sets of updates is obviously very similar to the edits we made to the C, C, and C -classes created in Part 3. +classes created in Chapter 3. Note that we do not need to make any change to the -C schema file. It simply tells DBIC to -load all of the result class files it finds in below the -C directory, so it will automatically pick -up our new table information. +C schema file. It simply tells DBIC to load all +of the Result Class and ResultSet Class files it finds in below the +C directory, so it will automatically pick up our +new table information. =head2 Sanity-Check Reload of Development Server @@ -243,7 +245,7 @@ Look for the three new model objects in the startup debug output: '-------------------------------------------------------------------+----------' ... -Again, notice that your "result class" classes have been "re-loaded" +Again, notice that your "Result Class" classes have been "re-loaded" by Catalyst under C. @@ -252,23 +254,23 @@ by Catalyst under C. Edit C and update it as follows (everything below C is new): - __PACKAGE__->setup(qw/ - -Debug - ConfigLoader - Static::Simple + # Load plugins + use Catalyst qw/-Debug + ConfigLoader + Static::Simple - StackTrace + StackTrace - Authentication + Authentication - Session - Session::Store::FastMmap - Session::State::Cookie - /); + Session + Session::Store::FastMmap + Session::State::Cookie + /; -B As discussed in MoreCatalystBasics, different versions of -C have used a variety of methods to load the plugins. -You put the plugins in the C statement if you prefer. +B As discussed in MoreCatalystBasics, different versions of +C have used a variety of methods to load the plugins. +You can put the plugins in the C statement if you prefer. The C plugin supports Authentication while the C plugins are required to maintain state across multiple HTTP @@ -294,65 +296,27 @@ backed session store). =head2 Configure Authentication -Although C<__PACKAGE__-Econfig(name =E 'value');> is still -supported, newer Catalyst applications tend to place all configuration -information in C and automatically load this information -into Cconfig> using the -L plugin. - -First, as noted in Part 3 of the tutorial, Catalyst has recently -switched from a default config file format of YAML to -C (an apache-like format). In case you are using a -version of Catalyst earlier than v5.7014, delete the C, or -convert it to .conf format using the TIP in -L; then simply follow the -directions below to create a new C file. - -Here, we need to load several parameters that tell -L -where to locate information in your database. To do this, edit the -C file and update it to match: - - # rename this file to MyApp.yml and put a : in front of "name" if - # you want to use yaml like in old versions of Catalyst - name MyApp - - default_realm dbic - - - - # Note: this first definition would be the same as setting - # __PACKAGE__->config->{authentication}->{realms}->{dbic} - # ->{credential} = 'Password' in lib/MyApp.pm - # - # Specify that we are going to do password-based auth - class Password - # This is the name of the field in the users table with the - # password stored in it - password_field password - # We are using an unencrypted password for now - password_type clear - - - # Use DBIC to retrieve username, password & role information - class DBIx::Class - # This is the model object created by Catalyst::Model::DBIC - # from your schema (you created 'MyApp::Schema::User' but as - # the Catalyst startup debug messages show, it was loaded as - # 'MyApp::Model::DB::Users'). - # NOTE: Omit 'MyApp::Model' here just as you would when using - # '$c->model("DB::Users)' - user_class DB::Users - - - - - -Inline comments in the code above explain how each field is being used. - -Note that you can use many other config file formats with catalyst. -See L -for details. +There are a variety of way to provide configuration information to +L. +Here we will use +L +because it automatically sets a reasonable set of defaults for us. Open +C and place the following text above the call to +C<__PACKAGE__-Esetup();>: + + # Configure SimpleDB Authentication + __PACKAGE__->config->{'Plugin::Authentication'} = { + default => { + class => 'SimpleDB', + user_model => 'DB::Users', + password_type => 'clear', + }, + }; + +We could have placed this configuration in C, but placing +it in C is probably a better place since it's not likely +something that users of your application will want to change during +deployment. =head2 Add Login and Logout Controllers @@ -362,16 +326,16 @@ Use the Catalyst create script to create two stub controller files: $ script/myapp_create.pl controller Login $ script/myapp_create.pl controller Logout -B You could easily use a single controller here. For example, -you could have a C controller with both C and C -actions. Remember, Catalyst is designed to be very flexible, and leaves -such matters up to you, the designer and programmer. +You could easily use a single controller here. For example, you could +have a C controller with both C and C actions. +Remember, Catalyst is designed to be very flexible, and leaves such +matters up to you, the designer and programmer. -Then open C, locate the C method (or C if you are using an -older version of Catalyst) that was automatically inserted by the -helpers when we created the Login controller above, and update the -definition of C to match: +Then open C, locate the +C method (or C if you +are using an older version of Catalyst) that was automatically +inserted by the helpers when we created the Login controller above, +and update the definition of C to match: =head2 index @@ -392,7 +356,8 @@ definition of C to match: if ($c->authenticate({ username => $username, password => $password } )) { # If successful, then let them use the application - $c->response->redirect($c->uri_for('/books/list')); + $c->response->redirect($c->uri_for( + $c->controller('Books')->action_for('list'))); return; } else { # Set an error message @@ -411,13 +376,13 @@ will stay at the login page and receive an error message. If the C and C values are not present in the form, the user will be taken to the empty login form. -Note that we could have used something like C, -however, it is generally recommended (partly for historical reasons, -and partly for code clarity) only to use C in -C, and then mainly to generate the 404 not +Note that we could have used something like "C", +however, it is generally recommended (partly for historical reasons, +and partly for code clarity) only to use C in +C, and then mainly to generate the 404 not found page for the application. -Instead, we are using C here to +Instead, we are using "C" here to specifically match the URL C. C actions (aka, "literal actions") create URI matches relative to the namespace of the controller where they are defined. Although C supports @@ -524,63 +489,14 @@ the following method: return 1; } - -B Catalyst provides a number of different types of actions, -such as C, C, C and the new C. You -should refer to L for -a more detailed explanation, but the following bullet points provide a -quick introduction: - -=over 4 - -=item * - -The majority of application have traditionally used C actions -for items that respond to user requests and C actions for -those that do not directly respond to user input. - -=item * - -Newer Catalyst applications tend to use C actions and the -C attribute because of their power and flexibility. You can -specify the path to match relative to the namespace of the current -module as an argument to C. For example C in -C would match on the URL -C but C would -match on C. - -=item * - -Automatic "chaining" of actions by the dispatcher is a powerful -feature that allows multiple methods to handle a single URL. See -L -for more information on chained actions. - -=item * - -There are five types of build-in C actions: C, C, -C, C, and C. - -=item * - -With C, C, C, C private actions, only the -most specific action of each type will be called. For example, if you -define a C action in your controller it will I a -C action in your application/root controller -- I the -action in your controller will be called. - -=item * - -Unlike the other actions where only a single method is called for each -request, I auto action along the chain of namespaces will be -called. Each C action will be called I. - -=back - -By placing the authentication enforcement code inside the C method -of C (or C), it will be -called for I request that is received by the entire application. +As discussed in +L, +every C method from the application/root controller down to the +most specific controller will be called. By placing the +authentication enforcement code inside the C method of +C (or C), it will be +called for I request that is received by the entire +application. =head2 Displaying Content Only to Authenticated Users @@ -590,6 +506,7 @@ changes depending on whether the user has authenticated yet. To do this, open C in your editor and add the following lines to the bottom of the file: + ...

[% # This code illustrates how certain parts of the TT @@ -624,24 +541,33 @@ running) and restart it: $ script/myapp_server.pl -B If you are having issues with authentication on -Internet Explorer, be sure to check the system clocks on both your -server and client machines. Internet Explorer is very picky about -timestamps for cookies. Note that you can quickly sync an Ubuntu -system with the following command: +B If you are having issues with authentication on +Internet Explorer, be sure to check the system clocks on both your +server and client machines. Internet Explorer is very picky about +timestamps for cookies. You can quickly sync a Debian system by +installing the "ntpdate" package: + + sudo aptitude -y install ntpdate - sudo ntpdate ntp.ubuntu.com +And then run the following command: -Or possibly try C (to us an -unpriviledged port) or C (to try a -different server in case the Ubuntu NTP server is down). + sudo ntpdate-debian -Now trying going to L and you should -be redirected to the login page, hitting Shift+Reload or Ctrl+Reload -if necessary (the "You are already logged in" message should I -appear -- if it does, click the C button and try again). Note -the C<***Root::auto User not found...> debug message in the -development server output. Enter username C and password +Or, depending on your firewall configuration: + + sudo ntpdate-debian -u + +Note: NTP can be a little more finicky about firewalls because it uses +UDP vs. the more common TCP that you see with most Internet protocols. +Worse case, you might have to manually set the time on your development +box instead of using NTP. + +Now trying going to L and you should +be redirected to the login page, hitting Shift+Reload or Ctrl+Reload +if necessary (the "You are already logged in" message should I +appear -- if it does, click the C button and try again). Note +the C<***Root::auto User not found...> debug message in the +development server output. Enter username C and password C, and you should be taken to the Book List page. Open C and add the following lines to the @@ -649,7 +575,7 @@ bottom (below the closing tag):

Login - Create + Create

Reload your browser and you should now see a "Login" and "Create" links @@ -665,108 +591,153 @@ need to log in to use this application." =head1 USING PASSWORD HASHES -In this section we increase the security of our system by converting -from cleartext passwords to SHA-1 password hashes. +In this section we increase the security of our system by converting +from cleartext passwords to SHA-1 password hashes that include a +random "salt" value to make them extremely difficult to crack with +dictionary and "rainbow table" attacks. B This section is optional. You can skip it and the rest of the tutorial will function normally. -Note that even with the techniques shown in this section, the browser +Be aware that even with the techniques shown in this section, the browser still transmits the passwords in cleartext to your application. We are just avoiding the I of cleartext passwords in the database by -using a SHA-1 hash. If you are concerned about cleartext passwords +using a salted SHA-1 hash. If you are concerned about cleartext passwords between the browser and your application, consider using SSL/TLS, made easy with the Catalyst plugin Catalyst::Plugin:RequireSSL. -=head2 Get a SHA-1 Hash for the Password +=head2 Install DBIx::Class::EncodedColumn -Catalyst uses the C module to support a variety of hashing -algorithms. Here we will use SHA-1 (SHA = Secure Hash Algorithm). -First, we should compute the SHA-1 hash for the "mypass" password we are -using. The following command-line Perl script provides a "quick and -dirty" way to do this: +L provides features +that can greatly simplify the maintenance of passwords. It's currently +not available as a .deb package in the normal Debian repositories, so let's +install it directly from CPAN: - $ perl -MDigest::SHA -e 'print Digest::SHA::sha1_hex("mypass"), "\n"' - e727d1464ae12436e899a726da5b2f11d8381b26 - $ + $ sudo cpan DBIx::Class::EncodedColumn -B If you are following along in Ubuntu, you will need to install -C with the following command to run the example code above: - sudo aptitude install libdigest-sha-perl +=head2 Re-Run the DBIC::Schema Model Helper to Include DBIx::Class::EncodedColumn -B You should probably modify this code for production use to -not read the password from the command line. By having the script -prompt for the cleartext password, it avoids having the password linger -in forms such as your C<.bash_history> files (assuming you are using -BASH as your shell). An example of such a script can be found in -Appendix 3. +Next, we can re-run the model helper to have it include +L in all of the +Result Classes it generates for us. Simply use the same command we +saw in Chapters 3 and 4, but add C<,EncodedColumn> to the C +argument: + $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ + create=static components=TimeStamp,EncodedColumn dbi:SQLite:myapp.db -=head2 Switch to SHA-1 Password Hashes in the Database +If you then open one of the Result Classes, you will see that it +includes EncodedColumn in the C line. Take a look at +C since that's the main class where we +want to use hashed and salted passwords: -Next, we need to change the C column of our C table to -store this hash value vs. the existing cleartext password. Open -C in your editor and enter: + __PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "EncodedColumn", "Core"); - -- - -- Convert passwords to SHA-1 hashes - -- - UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 1; - UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 2; - UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 3; - -Then use the following command to update the SQLite database: - - $ sqlite3 myapp.db < myapp03.sql - -B We are using SHA-1 hashes here, but many other hashing -algorithms are supported. See C for more information. - - -=head2 Enable SHA-1 Hash Passwords in -C - -Edit C and update it to match (the C and -C are new, everything else is the same): - - # rename this file to MyApp.yml and put a : in front of "name" if - # you want to use yaml like in old versions of Catalyst - name MyApp - - default_realm dbic - - - - # Note this first definition would be the same as setting - # __PACKAGE__->config->{authentication}->{realms}->{dbic} - # ->{credential} = 'Password' in lib/MyApp.pm - # - # Specify that we are going to do password-based auth - class Password - # This is the name of the field in the users table with the - # password stored in it - password_field password - # Switch to more secure hashed passwords - password_type hashed - # Use the SHA-1 hashing algorithm - password_hash_type SHA-1 - - - # Use DBIC to retrieve username, password & role information - class DBIx::Class - # This is the model object created by Catalyst::Model::DBIC - # from your schema (you created 'MyApp::Schema::User' but as - # the Catalyst startup debug messages show, it was loaded as - # 'MyApp::Model::DB::Users'). - # NOTE: Omit 'MyApp::Model' here just as you would when using - # '$c->model("DB::Users)' - user_class DB::Users - - - - + +=head2 Modify the "password" Column to Use EncodedColumn + +Open the file C and enter the following +text below the "# DO NOT MODIFY THIS OR ANYTHING ABOVE!" line but above +the closing "1;": + + # Have the 'password' column use a SHA-1 hash and 10-character salt + # with hex encoding; Generate the 'check_password" method + __PACKAGE__->add_columns( + 'password' => { + data_type => "TEXT", + size => undef, + encode_column => 1, + encode_class => 'Digest', + encode_args => {salt_length => 10}, + encode_check_method => 'check_password', + }, + ); + +This redefines the automatically generated definition for the password +fields at the top of the Result Class file to now use EncodedColumn +logic (C is set to 1). C can be set to +either C to use +L, +or C for +L. +C is then used to customize the type of Digest you +selected. Here we only specified the size of the salt to use, but +we could have also modified the hashing algorithm ('SHA-256' is +the default) and the format to use ('base64' is the default, but +'hex' and 'binary' are other options). To use these, you could +change the C to something like: + + encode_args => {algorithm => 'SHA-1', + format => 'hex', + salt_length => 10}, + + +=head2 Load Hashed Passwords in the Database + +Next, let's create a quick script to load some hashed and salted passwords +into the C column of our C table. Open the file +C in your editor and enter the following text: + + #!/usr/bin/perl + + use strict; + use warnings; + + use MyApp::Schema; + + my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db'); + + my @users = $schema->resultset('Users')->all; + + foreach my $user (@users) { + $user->password('mypass'); + $user->update; + } + +EncodedColumn lets us simple call C<$user->check_password($password)> +to see if the user has supplied the correct password, or, as we show +above, call C<$user->update($new_password)> to update the hashed +password stored for this user. + +Then run the following command: + + $ perl -Ilib set_hashed_passwords.pl + +We had to use the C<-Ilib> arguement to tell perl to look under the +C directory for our C model. + +Then dump the users table to verify that it worked: + + $ sqlite3 myapp.db "select * from users" + 1|test01|38d3974fa9e9263099f7bc2574284b2f55473a9bM=fwpX2NR8|t01@na.com|Joe|Blow|1 + 2|test02|6ed8586587e53e0d7509b1cfed5df08feadc68cbMJlnPyPt0I|t02@na.com|Jane|Doe|1 + 3|test03|af929a151340c6aed4d54d7e2651795d1ad2e2f7UW8dHoGv9z|t03@na.com|No|Go|0 + +As you can see, the passwords are much harder to steal from the +database. Also note that this demonstrates how to use a DBIx::Class +model outside of your web application -- a very useful feature in many +situations. + + +=head2 Enable Hashed and Salted Passwords + +Edit C and update it to match the following text (the only change +is to the C field): + + # Configure SimpleDB Authentication + __PACKAGE__->config->{'Plugin::Authentication'} = { + default => { + class => 'SimpleDB', + user_model => 'DB::Users', + password_type => 'self_check', + }, + }; + +The use of C will cause +Catalyst::Plugin::Authentication::Store::DBIC to call the +C method we enabled on our C columns. =head2 Try Out the Hashed Passwords @@ -777,21 +748,21 @@ running) and restart it: $ script/myapp_server.pl You should now be able to go to L and -login as before. When done, click the "Logout" link on the login page +login as before. When done, click the "logout" link on the login page (or point your browser at L). =head1 USING THE SESSION FOR FLASH -As discussed in Part 3 of the tutorial, C allows you to set -variables in a way that is very similar to C, but it will -remain set across multiple requests. Once the value is read, it -is cleared (unless reset). Although C has nothing to do with -authentication, it does leverage the same session plugins. Now that -those plugins are enabled, let's go back and improve the "delete -and redirect with query parameters" code seen at the end of the -L part of the -tutorial. +As discussed in the previous chapter of the tutorial, C allows +you to set variables in a way that is very similar to C, but it +will remain set across multiple requests. Once the value is read, it +is cleared (unless reset). Although C has nothing to do with +authentication, it does leverage the same session plugins. Now that +those plugins are enabled, let's go back and update the "delete and +redirect with query parameters" code seen at the end of the L chapter of the tutorial to +take advantage of C. First, open C and modify C to match the following (everything after the model search line of code @@ -803,18 +774,18 @@ has changed): =cut - sub delete : Local { - # $id = primary key of book to delete - my ($self, $c, $id) = @_; + sub delete :Chained('object') :PathPart('delete') :Args(0) { + my ($self, $c) = @_; - # Search for the book and then delete it - $c->model('DB::Books')->search({id => $id})->delete_all; + # Use the book object saved by 'object' and delete it along + # with related 'book_authors' entries + $c->stash->{object}->delete; # Use 'flash' to save information across requests until it's read $c->flash->{status_msg} = "Book deleted"; # Redirect the user back to the list page - $c->response->redirect($c->uri_for('/books/list')); + $c->response->redirect($c->uri_for($self->action_for('list'))); } Next, open C and update the TT code to pull from @@ -830,15 +801,15 @@ flash vs. the C query parameter: ... -Although the sample above only shows the C div, leave the +Although the sample above only shows the C div, leave the rest of the file intact -- the only change we made to the C -was to add "C<|| c.request.params.status_msg>" to the +was to add "C<|| c.request.params.status_msg>" to the Cspan class="message"E> line. =head2 Try Out Flash -Restart the development server and point your browser to +Restart the development server, log in, and then point your browser to L to create an extra several books. Click the "Return to list" link and delete one of the "Test" books you just added. The C mechanism should retain our @@ -855,18 +826,17 @@ information. =head2 Switch To Flash-To-Stash -Although the a use of flash above is certainly an improvement over the -C we employed in Part 4 of the tutorial, the +Although the a use of flash above works well, the C statement is a little ugly. A nice alternative is to use the C feature that automatically copies the content of flash to stash. This makes your controller and template code work regardless of where it was directly access, a -forward, or a redirect. To enable C, you can either +forward, or a redirect. To enable C, you can either set the value in C by changing the default C<__PACKAGE__-Econfig> setting to something like: __PACKAGE__->config( - name => 'MyApp', + name => 'MyApp', session => {flash_to_stash => 1} );