Cleaned up Tut files; no substantive changes
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Tutorial / Authentication.pod
1 =head1 NAME
2
3 Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 4: Authentication
4
5
6
7 =head1 OVERVIEW
8
9 This is B<Part 4 of 9> for the Catalyst tutorial.
10
11 L<Tutorial Overview|Catalyst::Manual::Tutorial>
12
13 =over 4
14
15 =item 1
16
17 L<Introduction|Catalyst::Manual::Tutorial::Intro>
18
19 =item 2
20
21 L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
22
23 =item 3
24
25 L<Basic CRUD|Catalyst::Manual::Tutorial_BasicCRUD>
26
27 =item 4
28
29 B<Authentication>
30
31 =item 5
32
33 L<Authorization|Catalyst::Manual::Tutorial::Authorization>
34
35 =item 6
36
37 L<Debugging|Catalyst::Manual::Tutorial::Debugging>
38
39 =item 7
40
41 L<Testing|Catalyst::Manual::Tutorial::Testing>
42
43 =item 8
44
45 L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
46
47 =item 9
48
49 L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
50
51 =back
52
53
54
55 =head1 DESCRIPTION
56
57 Now that we finally have a simple yet functional application, we can
58 focus on providing authentication (with authorization coming in Part 5).
59
60 This part of the tutorial is divided into two main sections: 1) basic,
61 cleartext authentication and 2) hash-based authentication.
62
63 B<TIP>: Note that all of the code for this part of the tutorial can be
64 pulled from the Catalyst Subversion repository in one step with the
65 following command:
66
67     svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
68     IMPORTANT: Does not work yet.  Will be completed for final version.
69
70
71
72 =head1 BASIC AUTHENTICATION
73
74 This section explores how add authentication logic to a Catalyst application.
75
76
77 =head2 Add Users and Roles to the Database
78
79 First, we add both user and role information to the database (we add the
80 role information here although it will not be used until the
81 authorization section, Part 5).  Create a new SQL script file by opening
82 C<myapp02.sql> in your editor and insert:
83
84     --
85     -- Add users and roles tables, along with a many-to-many join table
86     --
87     CREATE TABLE users (
88             id            INTEGER PRIMARY KEY,
89             username      TEXT,
90             password      TEXT,
91             email_address TEXT,
92             first_name    TEXT,
93             last_name     TEXT,
94             active        INTEGER
95     );
96     CREATE TABLE roles (
97             id   INTEGER PRIMARY KEY,
98             role TEXT
99     );
100     CREATE TABLE user_roles (
101             user_id INTEGER,
102             role_id INTEGER,
103             PRIMARY KEY (user_id, role_id)
104     );
105     --
106     -- Load up some initial test data
107     --
108     INSERT INTO users VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe',  'Blow', 1);
109     INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe',  1);
110     INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@na.com', 'No',   'Go',   0);
111     INSERT INTO roles VALUES (1, 'user');
112     INSERT INTO roles VALUES (2, 'admin');
113     INSERT INTO user_roles VALUES (1, 1);
114     INSERT INTO user_roles VALUES (1, 2);
115     INSERT INTO user_roles VALUES (2, 1);
116     INSERT INTO user_roles VALUES (3, 1);
117
118 Then load this into the C<myapp.db> database with the following command:
119
120     $ sqlite3 myapp.db < myapp02.sql
121
122
123 =head2 Add User and Role Information to Dbic Schema
124
125 This step adds DBIC-based classes for the user-related database tables
126 (the role information will not be used until the Part 5):
127
128 Edit C<lib/MyAppDB.pm> and update the contents to match (only the
129 C<MyAppDB =E<gt> [qw/Book BookAuthor Author User UserRole Role/]> line
130 has changed):
131
132     package MyAppDB;
133     
134     =head1 NAME 
135     
136     MyAppDB -- DBIC Schema Class
137     
138     =cut
139     
140     # Our schema needs to inherit from 'DBIx::Class::Schema'
141     use base qw/DBIx::Class::Schema/;
142     
143     # Need to load the DB Model classes here.
144     # You can use this syntax if you want:
145     #    __PACKAGE__->load_classes(qw/Book BookAuthor Author User UserRole Role/);
146     # Also, if you simply want to load all of the classes in a directory
147     # of the same name as your schema class (as we do here) you can use:
148     #    __PACKAGE__->load_classes(qw//);
149     # But the variation below is more flexible in that it can be used to 
150     # load from multiple namespaces.
151     __PACKAGE__->load_classes({
152         MyAppDB => [qw/Book BookAuthor Author User UserRole Role/]
153     });
154     
155     1;
156
157
158 =head2 Create New "Result Source Objects"
159
160 Create the following three files with the content shown below.
161
162 C<lib/MyAppDB/User.pm>:
163
164     package MyAppDB::User;
165     
166     use base qw/DBIx::Class/;
167     
168     # Load required DBIC stuff
169     __PACKAGE__->load_components(qw/PK::Auto Core/);
170     # Set the table name
171     __PACKAGE__->table('users');
172     # Set columns in table
173     __PACKAGE__->add_columns(qw/id username password email_address first_name last_name/);
174     # Set the primary key for the table
175     __PACKAGE__->set_primary_key('id');
176     
177     #
178     # Set relationships:
179     #
180     
181     # has_many():
182     #   args:
183     #     1) Name of relationship, DBIC will create accessor with this name
184     #     2) Name of the model class referenced by this relationship
185     #     3) Column name in *foreign* table
186     __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'user_id');
187     
188     
189     =head1 NAME
190     
191     MyAppDB::User - A model object representing a person with access to the system.
192     
193     =head1 DESCRIPTION
194     
195     This is an object that represents a row in the 'users' table of your application
196     database.  It uses DBIx::Class (aka, DBIC) to do ORM.
197     
198     For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
199     Offline utilities may wish to use this class directly.
200     
201     =cut
202     
203     1;
204
205
206 C<lib/MyAppDB/Role.pm>:
207
208     package MyAppDB::Role;
209     
210     use base qw/DBIx::Class/;
211     
212     # Load required DBIC stuff
213     __PACKAGE__->load_components(qw/PK::Auto Core/);
214     # Set the table name
215     __PACKAGE__->table('roles');
216     # Set columns in table
217     __PACKAGE__->add_columns(qw/id role/);
218     # Set the primary key for the table
219     __PACKAGE__->set_primary_key('id');
220     
221     #
222     # Set relationships:
223     #
224     
225     # has_many():
226     #   args:
227     #     1) Name of relationship, DBIC will create accessor with this name
228     #     2) Name of the model class referenced by this relationship
229     #     3) Column name in *foreign* table
230     __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'role_id');
231     
232     
233     =head1 NAME
234     
235     MyAppDB::Role - A model object representing a class of access permissions to 
236     the system.
237     
238     =head1 DESCRIPTION
239     
240     This is an object that represents a row in the 'roles' table of your 
241     application database.  It uses DBIx::Class (aka, DBIC) to do ORM.
242     
243     For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
244     "Offline" utilities may wish to use this class directly.
245     
246     =cut
247     
248     1;
249
250
251 C<lib/MyAppDB/UserRole.pm>:
252
253     package MyAppDB::UserRole;
254     
255     use base qw/DBIx::Class/;
256     
257     # Load required DBIC stuff
258     __PACKAGE__->load_components(qw/PK::Auto Core/);
259     # Set the table name
260     __PACKAGE__->table('user_roles');
261     # Set columns in table
262     __PACKAGE__->add_columns(qw/user_id role_id/);
263     # Set the primary key for the table
264     __PACKAGE__->set_primary_key(qw/user_id role_id/);
265     
266     #
267     # Set relationships:
268     #
269     
270     # belongs_to():
271     #   args:
272     #     1) Name of relationship, DBIC will create accessor with this name
273     #     2) Name of the model class referenced by this relationship
274     #     3) Column name in *this* table
275     __PACKAGE__->belongs_to(user => 'MyAppDB::User', 'user_id');
276     
277     # belongs_to():
278     #   args:
279     #     1) Name of relationship, DBIC will create accessor with this name
280     #     2) Name of the model class referenced by this relationship
281     #     3) Column name in *this* table
282     __PACKAGE__->belongs_to(role => 'MyAppDB::Role', 'role_id');
283     
284     
285     =head1 NAME
286     
287     MyAppDB::UserRole - A model object representing the JOIN between Users and Roles.
288     
289     =head1 DESCRIPTION
290     
291     This is an object that represents a row in the 'user_roles' table of your application
292     database.  It uses DBIx::Class (aka, DBIC) to do ORM.
293     
294     You probably won't need to use this class directly -- it will be automatically
295     used by DBIC where joins are needed.
296     
297     For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
298     Offline utilities may wish to use this class directly.
299     
300     =cut
301     
302     1;
303
304 The code for these three result source classes is obviously very familiar to the C<Book>, C<Author>, and C<BookAuthor> classes created in Part 2.
305
306
307 =head2 Sanity-Check Reload of Development Server
308
309 We aren't ready to try out the authentication just yet; we only want to do a quick check to be sure our model loads correctly.  Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
310
311     $ script/myapp_server.pl
312
313 Look for the three new model objects in the startup debug output:
314
315     ...
316      .-------------------------------------------------------------------+----------.
317     | Class                                                             | Type     |
318     +-------------------------------------------------------------------+----------+
319     | MyApp::Controller::Books                                          | instance |
320     | MyApp::Controller::Root                                           | instance |
321     | MyApp::Model::MyAppDB                                             | instance |
322     | MyApp::Model::MyAppDB::Author                                     | class    |
323     | MyApp::Model::MyAppDB::Book                                       | class    |
324     | MyApp::Model::MyAppDB::BookAuthor                                 | class    |
325     | MyApp::Model::MyAppDB::Role                                       | class    |
326     | MyApp::Model::MyAppDB::User                                       | class    |
327     | MyApp::Model::MyAppDB::UserRole                                   | class    |
328     | MyApp::View::TT                                                   | instance |
329     '-------------------------------------------------------------------+----------'
330     ...
331
332 Again, notice that your "result source" classes have been "re-loaded" by Catalyst under C<MyApp::Model>.
333
334
335 =head2 Include Authentication and Session Plugins
336
337 Edit C<lib/MyApp.pm> and update it as follows (everything below C<DefaultEnd> is new):
338
339     use Catalyst qw/
340             -Debug
341             ConfigLoader
342             Static::Simple
343             
344             Dumper
345             StackTrace
346             DefaultEnd
347             
348             Authentication
349             Authentication::Store::DBIC
350             Authentication::Credential::Password
351             
352             Session
353             Session::Store::FastMmap
354             Session::State::Cookie
355             /;
356
357 The three C<Authentication> plugins work together to support
358 Authentication while the C<Session> plugins are required to maintain
359 state across multiple HTTP requests.  Note that there are several
360 options for L<Session::Store|Catalyst::Plugin::Session::Store> (although
361 L<Session::Store::FastMmap|Catalyst::Plugin::Session::Store::FastMmap>
362 is generally a good choice if you are on Unix; try
363 L<Cache::FileCache|Catalyst::Plugin::Cache::FileCache> if you are on
364 Win32) -- consult L<Session::Store|Catalyst::Plugin::Session::Store> and
365 its subclasses for additional information.
366
367
368 =head2 Configure Authentication
369
370 Although C<__PACKAGE__-E<gt>config(name =E<gt> 'value');> is still
371 supported, newer Catalyst applications tend to place all configuration
372 information in C<myapp.yml> and automatically load this information into
373 C<MyApp-E<gt>config> using the
374 L<ConfigLoader|Catalyst::Plugin::ConfigLoader> plugin.
375
376 Edit the C<myapp.yml> YAML and update it to match:
377
378     ---
379     name: MyApp
380     authentication:
381         dbic:
382             # Note this first definition would be the same as setting
383             # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
384             # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
385             #
386             # This is the model object created by Catalyst::Model::DBIC from your
387             # schema (you created 'MyAppDB::User' but as the Catalyst startup
388             # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
389             # NOTE: Omit 'MyAppDB::Model' to avoid a component lookup issue in Catalyst 5.66
390             user_class: MyAppDB::User
391             # This is the name of the field in your 'users' table that contains the user's name
392             user_field: username
393             # This is the name of the field in your 'users' table that contains the password
394             password_field: password
395             # Other options can go here for hashed passwords
396
397 Inline comments in the code above explain how each field is being used.
398
399 B<TIP>: Although YAML uses a very simple and easy-to-ready format, it
400 does require the use of a consistent level of indenting.  Be sure you
401 line up everything on a given 'level' with the same number of indents.
402 Also, be sure not to use C<tab> characters (YAML does not support them
403 because they are handled inconsistently across editors).
404
405
406 =head2 Add Login and Logout Controllers
407
408 Use the Catalyst create script to create two stub controller files:
409
410     $ script/myapp_create.pl controller Login
411     $ script/myapp_create.pl controller Logout
412
413 B<NOTE>: You could easily use a single controller here.  For example,
414 you could have a C<User> controller with both C<login> and C<logout>
415 actions.  Remember, Catalyst is designed to be very flexible, and leaves
416 such matters up to you, the designer and programmer.
417
418 Then open C<lib/MyApp/Controller/Login.pm> and add:
419
420     =head2 default
421     
422     Login logic
423     
424     =cut
425     
426     sub default : Private {
427         my ($self, $c) = @_;
428     
429         # Get the username and password from form
430         my $username = $c->request->params->{username} || "";
431         my $password = $c->request->params->{password} || "";
432     
433         # If the username and password values were found in form
434         if ($username && $password) {
435             # Attempt to log the user in
436             if ($c->login($username, $password)) {
437                 # If successful, then let them use the application
438                 $c->response->redirect($c->uri_for('/books/list'));
439                 return;
440             } else {
441                 # Set an error message
442                 $c->stash->{error_msg} = "Bad username or password.";
443             }
444         }
445     
446         # If either of above don't work out, send to the login page
447         $c->stash->{template} = 'login.tt2';
448     }
449
450 This controller fetches the C<username> and C<password> values from the
451 login form and attempts to perform a login.  If successful, it redirects
452 the user to the book list page.  If the login fails, the user will stay
453 at the login page but receive an error message.  If the C<username> and
454 C<password> values are not present in the form, the user will be taken
455 to the empty login form.
456
457 Next, create a corresponding method in C<lib/MyApp/Controller/Logout.pm>:
458
459     =head2 default
460     
461     Logout logic
462     
463     =cut
464     
465     sub default : Private {
466         my ($self, $c) = @_;
467     
468         # Clear the user's state
469         $c->logout;
470     
471         # Send the user to the starting
472         $c->response->redirect($c->uri_for('/'));
473     }
474
475
476 =head2 Add a Login Form TT Template Page
477
478 Create a login form by opening C<root/src/login.tt2> and inserting:
479
480     [% META title = 'Login' %]
481     
482     <!-- Login form -->
483     <form method="post" action=" [% Catalyst.uri_for('/login') %] ">
484       <table>
485         <tr>
486           <td>Username:</td>
487           <td><input type="text" name="username" size="40" /></td>
488         </tr>
489         <tr>
490           <td>Password:</td>
491           <td><input type="password" name="password" size="40" /></td>
492         </tr>
493         <tr>
494           <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
495         </tr>
496       </table>
497     </form>
498
499
500 =head2 Add Valid User Check
501
502 We need something that provides enforcement for the authentication
503 mechanism -- a I<global> mechanism that prevents users who have not
504 passed authentication from reaching any pages except the login page.
505 This is generally done via an C<auto> action/method (prior to Catalyst
506 v5.66, this sort of thing would go in C<MyApp.pm>, but starting in
507 v5.66, the preferred location is C<lib/MyApp/Controller/Root.pm>).
508
509 Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert the following method:
510
511     =head2 auto
512     
513     Check if there is a user and, if not, forward to login page
514     
515     =cut
516     
517     # Note that 'auto' runs after 'begin' but before your actions and that
518     # 'auto' "chain" (all from application path to most specific class are run)
519     sub auto : Private {
520         my ($self, $c) = @_;
521     
522         # Allow unauthenticated users to reach the login page
523         if ($c->request->path =~ /login/) {
524             return 1;
525         }
526     
527         # If a user doesn't exist, force login
528         if (!$c->user_exists) {
529             # Dump a log message to the development server debug output
530             $c->log->debug('***Root::auto User not found, forwarding to /login');
531             # Redirect the user to the login page
532             $c->response->redirect($c->uri_for('/login'));
533             # Return 0 to cancel 'post-auto' processing and prevent use of application
534             return 0;
535         }
536     
537         # User found, so return 1 to continue with processing after this 'auto'
538         return 1;
539     }
540
541 B<Note:> Catalyst provides a number of different types of actions, such
542 as C<Local>, C<Regex>, and C<Private>.  You should refer to
543 L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> for a more detailed
544 explanation, but the following bullet points provide a quick
545 introduction:
546
547 =over 4
548
549 =item *
550
551 The majority of application use C<Local> actions for items that respond
552 to user requests and C<Private> actions for those that do not directly
553 respond to user input.
554
555 =item *
556
557 There are five types of C<Private> actions: C<begin>, C<end>,
558 C<default>, C<index>, and C<auto>.
559
560 =item *
561
562 Unlike the other private C<Private> actions where only a single method
563 is called for each request, I<every> auto action along the chain of
564 namespaces will be called.
565
566 =back
567
568 By placing the authentication enforcement code inside the C<auto> method
569 of C<lib/MyApp/Controller/Root.pm> (or C<lib/MyApp.pm>), it will be
570 called for I<every> request that is received by the entire application.
571
572
573 =head2 Displaying Content Only to Authenticated Users
574
575 Let's say you want to provide some information on the login page that
576 changes depending on whether the user has authenticated yet.  To do
577 this, open C<root/src/login.tt2> in your editor and add the following
578 lines to the bottom of the file:
579
580     <p>
581     [%
582        # This code illustrates how certain parts of the TT 
583        # template will only be shown to users who have logged in
584     %]
585     [% IF Catalyst.user %]
586         Please Note: You are already logged in as '[% Catalyst.user.username %]'.
587         You can <a href="[% Catalyst.uri_for('/logout') %]">logout</a> here.
588     [% ELSE %]
589         You need to log in to use this application.
590     [% END %]
591     [%#
592        Note that this whole block is a comment because the "#" appears
593        immediate after the "[%" (with no spaces in between).  Although it 
594        can be a handy way to temporarily "comment out" a whole block of 
595        TT code, it's probably a little too subtle for use in "normal" 
596        comments.
597     %]
598
599 Although most of the code is comments, the middle few lines provide a
600 "you are already logged in" reminder if the user returns to the login
601 page after they have already authenticated.  For users who have not yet
602 authenticated, a "You need to log in..." message is displayed (note the
603 use of an IF-THEN-ELSE construct in TT).
604
605
606 =head2 Try Out Authentication
607
608 Press C<Ctrl-C> to kill the previous server instance (if it's still
609 running) and restart it:
610
611     $ script/myapp_server.pl
612
613 B<IMPORTANT NOTE>: If you happen to be using Internet Explorer, you may
614 need to use the command C<script/myapp_server.pl -k> to enable the
615 keepalive feature in the development server.  Otherwise, the HTTP
616 redirect on successful login may not work correctly with IE (it seems to
617 work without -k if you are running the web browser and development
618 server on the same machine).  If you are using browser a browser other
619 than IE, it should work either way.  If you want to make keepalive the
620 default, you can edit C<script/myapp_server.pl> and change the
621 initialization value for C<$keepalive> to C<1>.  (You will need to do
622 this every time you create a new Catalyst application or rebuild the
623 C<myapp_server.pl> script.)
624
625 Now trying going to L<http://localhost:3000/books/list> and you should
626 be redirected to the login page, hitting Shift+Reload if necessary (the
627 "You are already logged in" message should I<not> appear -- if it does,
628 click the C<logout> button and try again).  Make note of the
629 C<***Root::auto User not found...> debug message in the development
630 server output.  Enter username C<test01> and password C<mypass>, and you
631 should be taken to the Book List page.
632
633 Open C< root/src/books/list.tt2> and add the following lines to the bottom:
634
635     <p>
636       <a href="[% Catalyst.uri_for('/login') %]">Login</a>
637       <a href="[% Catalyst.uri_for('form_create') %]">Create</a>
638     </p>
639
640 Reload your browser and you should now see a "Login" link at the bottom
641 of the page (as mentioned earlier, you can update template files without
642 reloading the development server).  Click this link to return to the
643 login page.  This time you I<should> see the "You are already logged in"
644 message.
645
646 Finally, click the C<You can logout here> link on the C</login> page.
647 You should stay at the login page, but the message should change to "You
648 need to log in to use this application."
649
650
651
652 =head1 USING PASSWORD HASHES
653
654 In this section we increase the security of our system by converting
655 from cleartext passwords to SHA-1 password hashes.
656
657 B<Note:> This section is optional.  You can skip it and the rest of the
658 tutorial will function normally.
659
660 Note that even with the techniques shown in this section, the browser
661 still transmits the passwords in cleartext to your application.  We are
662 just avoiding the I<storage> of cleartext passwords in the database by
663 using a SHA-1 hash.  If you are concerned about cleartext passwords
664 between the browser and your application, consider using SSL/TLS.
665
666
667 =head2 Get a SHA-1 Hash for the Password
668
669 Catalyst uses the C<Digest> module to support a variety of hashing
670 algorithms.  Here we will use SHA-1 (SHA = Secure Hash Algorithm).
671 First, we should compute the SHA-1 hash for the "mypass" password we are
672 using.  The following command-line Perl script provides a "quick and
673 dirty" way to do this:
674
675     $ perl -MDigest::SHA -e 'print Digest::SHA::sha1_hex("mypass"), "\n"'
676     e727d1464ae12436e899a726da5b2f11d8381b26
677     $
678
679
680 =head2 Switch to SHA-1 Password Hashes in the Database
681
682 Next, we need to change the C<password> column of our C<users> table to
683 store this hash value vs. the existing cleartext password.  Open
684 C<myapp03.sql> in your editor and enter:
685
686     --
687     -- Convert passwords to SHA-1 hashes
688     --
689     UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 1;
690     UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 2;
691     UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 3;
692
693 Then use the following command to update the SQLite database:
694
695     $ sqlite3 myapp.db < myapp03.sql
696
697 B<Note:> We are using SHA-1 hashes here, but many other hashing
698 algorithms are supported.  See C<Digest> for more information.
699
700
701 =head2 Enable SHA-1 Hash Passwords in
702 C<Catalyst::Plugin::Authentication::Store::DBIC>
703
704 Edit C<myapp.yml> and update it to match (the C<password_type> and
705 C<password_hash_type> are new, everything else is the same):
706
707     ---
708     name: MyApp
709     authentication:
710         dbic:
711             # Note this first definition would be the same as setting
712             # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
713             # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
714             #
715             # This is the model object created by Catalyst::Model::DBIC from your
716             # schema (you created 'MyAppDB::User' but as the Catalyst startup
717             # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
718             # NOTE: Omit 'MyAppDB::Model' to avoid a component lookup issue in Catalyst 5.66
719             user_class: MyAppDB::User
720             # This is the name of the field in your 'users' table that contains the user's name
721             user_field: username
722             # This is the name of the field in your 'users' table that contains the password
723             password_field: password
724             # Other options can go here for hashed passwords
725             # Enabled hashed passwords
726             password_type: hashed
727             # Use the SHA-1 hashing algorithm
728             password_hash_type: SHA-1
729
730
731 =head2 Try Out the Hashed Passwords
732
733 Press C<Ctrl-C> to kill the previous server instance (if it's still
734 running) and restart it:
735
736     $ script/myapp_server.pl
737
738 You should now be able to go to L<http://localhost:3000/books/list> and
739 login as before.  When done, click the "Logout" link on the login page
740 (or point your browser at L<http://localhost:3000/logout>).
741
742 =head1 AUTHOR
743
744 Kennedy Clark, C<hkclark@gmail.com>
745
746 Please report any errors, issues or suggestions to the author.
747
748 Copyright 2006, Kennedy Clark. All rights reserved.
749
750 This library is free software; you can redistribute it and/or modify it
751 under the same terms as Perl itself.
752
753 Version: .94
754