Commit | Line | Data |
649de93b |
1 | |
2 | =head1 NAME |
3 | |
4 | Catalyst::Plugin::Authentication::Internals - All about authentication Stores and Credentials |
5 | |
6 | =head1 INTRODUCTION |
7 | |
8 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> provides |
9 | a standard authentication interface to application developers using the |
10 | Catalyst framework. It is designed to allow application developers to use |
11 | various methods of user storage and credential verification. It is also |
12 | designed to provide for minimal change to the application when switching |
13 | between different storage and credential verification methods. |
14 | |
15 | While L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> |
16 | provides the interface to the application developer, the actual work of |
17 | verifying the credentials and retrieving users is delegated to separate |
18 | modules. These modules are called B<Credentials> and storage backends, or |
19 | B<Stores>, respectively. For authentication to function there must be at least |
20 | one credential and one storage backend. A pairing of a store and a credential |
21 | is referred to as a B<Realm>. There may be any number of realms defined for an |
22 | application, though most applications will not require more than one or two. |
23 | |
24 | The details of using this module can be found in the |
25 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> |
26 | documentation. |
27 | |
28 | What follows is an explanation of how the module functions internally and what |
29 | is required to implement a credential or a store. |
30 | |
31 | =head1 OVERVIEW |
32 | |
33 | There are two main entry points you need to be aware of when writing a store |
34 | or credential module. The first is initialization and the second is during the |
35 | actual call to the Catalyst application's authenticate method. |
36 | |
37 | =head2 INITIALIZATION |
38 | |
39 | When the authentication module is loaded, it reads it's configuration to |
40 | determine the realms to set up for the application and which realm is to be |
41 | the default. For each realm defined in the application's config, |
42 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> |
43 | instantiates both a new credential object and a new store object. See below |
44 | for the details of how credentials and stores are instantiated. |
45 | |
46 | NOTE: The instances created will remain active throughout the entire |
47 | lifetime of the application, and so should be relatively lightweight. |
48 | Care should be taken to ensure that they do not grow, or retain |
49 | information per request, because they will be involved in each |
50 | authentication request and could therefore substantially |
51 | hurt memory consumption over time. |
52 | |
53 | =head2 AUTHENTICATION |
54 | |
55 | When C<$c-E<gt>authenticate()> is called from within an application, the |
56 | objects created in the initialization process come into play. |
57 | C<$c-E<gt>authenticate()> takes two arguments. The first is a hash reference |
58 | containing all the information available about the user. This will be used to |
59 | locate the user in the store and verify the user's credentials. The second |
60 | argument is the realm to authenticate against. If the second argument is |
61 | omitted, the default realm is assumed. |
62 | |
63 | The main authentication module then locates the credential and store objects |
64 | for the realm specified and calls the credential object's C<authenticate()> |
65 | method. It provides three arguments, first the application object, or C<$c>, |
66 | then a reference to the store object, and finally the hashref provided in the |
67 | C<$c-E<gt>authenticate> call. The main authentication module expects the |
68 | return value to be a reference to a user object upon successful |
69 | authentication. If it receives anything aside from a reference, it is |
70 | considered to be an authentication failure. Upon success, the returned user is |
71 | marked as authenticated and the application can act accordingly, using |
72 | C<$c-E<gt>user> to access the authenticated user, etc. |
73 | |
74 | Astute readers will note that the main |
75 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> module |
76 | does not interact with the store in any way, save for passing a reference to |
77 | it to the credential. This is correct. The credential object is responsible |
78 | for obtaining the user from the provided store using information from the |
79 | userinfo hashref and/or data obtained during the credential verification |
80 | process. |
81 | |
82 | =head1 WRITING A STORE |
83 | |
84 | There are two parts to an authentication store, the backend and the user object. |
85 | |
86 | =head2 STORAGE BACKEND |
87 | |
88 | Writing a storage backend is actually quite simple. There are only five methods |
89 | that must be implemented. They are: |
90 | |
91 | new() - instantiates the store object |
92 | find_user() - locates a user using data contained in the hashref |
93 | for_session() - prepares a user to be stored in the session |
94 | from_session() - does any restoration required when obtaining a user from the session |
95 | user_supports() - provides information about what the user object supports |
96 | |
97 | =head3 STORE METHODS |
98 | |
99 | =over 4 |
100 | |
101 | =item new( $config, $app ) |
102 | |
103 | The C<new()> method is called only once, during the setup process of |
104 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>. The |
105 | first argument, C<$config>, is a hash reference containing the configuration |
106 | information for the store module. The second argument is a reference to the |
107 | Catalyst application. |
108 | |
109 | Note that when new() is called, Catalyst has not yet loaded the various |
110 | controller and model classes, nor is it definite that other plugins have |
111 | been loaded, so your new() method must not rely on any of those being |
112 | present. If any of this is required for your store to function, you should |
113 | defer that part of initialization until the first method call. |
4b69b736 |
114 | |
649de93b |
115 | The C<new()> method should return a blessed reference to your store object. |
116 | |
117 | =item find_user( $authinfo, $c ) |
118 | |
119 | This is the workhorse of any authentication store. It's job is to take the |
120 | information provided to it via the C<$authinfo> hashref and locate the user |
121 | that matches it. It should return a reference to a user object. A return value |
122 | of anything else is considered to mean no user was found that matched the |
123 | information provided. |
124 | |
125 | How C<find_user()> accomplishes it's job is entirely up to you, the author, as |
126 | is what $authinfo is required to contain. Many stores will simply use a |
127 | username element in $authinfo to locate the user, but more advanced functionality |
128 | is possible and you may bend the $authinfo to your needs. Be aware, however, that |
129 | both Credentials and Stores work with the same $authinfo hash, so take care to |
130 | avoid overlapping element names. |
131 | |
132 | Please note that this routine may be called numerous times in various |
133 | circumstances, and that a successful match for a user here does B<NOT> |
134 | necessarily constitute successful authentication. Your store class should |
135 | never assume this and in most cases C<$c> B<should not be modified> by your |
136 | store object. |
137 | |
138 | =item for_session( $c, $user ) |
139 | |
140 | This method is responsible for preparing a user object for storage in the session. |
141 | It should return information that can be placed in the session and later used to |
142 | restore a user object (using the C<from_session()> method). It should therefore |
143 | ensure that whatever information provided can be used by the C<from_session()> |
144 | method to locate the unique user being saved. Note that there is no guarantee |
145 | that the same Catalyst instance will receive both the C<for_session()> and |
146 | C<from_session()> calls. You should take care to provide information that can |
147 | be used to restore a user, regardless of the current state of the application. |
148 | A good rule of thumb is that if C<from_session()> can revive the user with the |
149 | given information even if the Catalyst application has just started up, you are |
150 | in good shape. |
151 | |
152 | =item from_session( $c, $frozenuser ) |
153 | |
154 | This method is called whenever a user is being restored from the session. |
155 | C<$frozenuser> contains the information that was stored in the session for the user. |
156 | This will under normal circumstances be the exact data your store returned from |
157 | the previous call to C<for_session()>. C<from_session()> should return a valid |
158 | user object. |
159 | |
160 | =item user_supports( $feature, ... ) |
161 | |
162 | This method allows credentials and other objects to inquire as to what the |
163 | underlying user object is capable of. This is pretty-well free-form and the |
164 | main purpose is to allow graceful integration with credentials and |
165 | applications that may provide advanced functionality based on whether the |
166 | underlying user object can do certain things. In most cases you will want to |
167 | pass this directly to the underlying user class' C<supports> method. Note that |
168 | this is used as a B<class> method against the user class and therefore must |
169 | be able to function without an instantiated user object. |
170 | |
171 | =back |
172 | |
173 | =head2 USER OBJECT |
174 | |
175 | The user object is an important piece of your store module. It will be the |
176 | part of the system that the application developer will interact with most. As |
177 | such, the API for the user object is very rigid. All user objects B<MUST> |
178 | inherit from |
179 | L<Catalyst::Plugin::Authentication::User|Catalyst::Plugin::Authentication::User>. |
180 | |
181 | =head3 USER METHODS |
182 | |
183 | The routines required by the |
184 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> plugin |
185 | are below. Note that of these, only get_object is strictly required, as the |
186 | L<Catalyst::Plugin::Authentication::User|Catalyst::Plugin::Authentication::User> |
187 | base class contains reasonable implementations of the rest. If you do choose |
4b69b736 |
188 | to implement only the C<get_object()> routine, please read the base class code |
189 | and documentation so that you fully understand how the other routines will be |
649de93b |
190 | implemented for you. |
191 | |
192 | Also, your user object can implement whatever additional methods you require |
193 | to provide the functionality you need. So long as the below are implemented, |
194 | and you don't overlap the base class' methods with incompatible routines, you |
195 | should experience no problems. |
196 | |
197 | =over 4 |
198 | |
199 | =item id( ) |
200 | |
201 | The C<id()> method should return a unique id (scalar) that can be used to |
202 | retreive this user from the store. Often this will be provided to the store's |
203 | C<find_user()> routine as C<id =E<gt> $user-E<gt>id> so you should ensure that your |
204 | store's C<find_user()> can cope with that. |
205 | |
206 | =item supports_features( ) |
207 | |
208 | This method should return a hashref of 'extra' features supported. This is for |
209 | more flexible integration with some Credentials / applications. It is not |
210 | required that you support anything, and returning C<undef> is perfectly |
211 | acceptable and in most cases what you will do. |
212 | |
213 | =item get( $fieldname ) |
214 | |
215 | This method should return the value of the field matching fieldname provided, |
216 | or undef if there is no field matching that fieldname. In most cases this will |
217 | access the underlying storage mechanism for the user data and return the |
218 | information. This is used as a standard method of accessing an authenticated |
219 | user's data, and MUST be implemented by all user objects. |
220 | |
221 | Note: There is no equivalent 'set' method. Each user class is likely |
222 | to vary greatly in how data must be saved and it is therefore impractical to |
223 | try to provide a standard way of accomplishing it. When an application |
224 | developer needs to save data, they should obtain the underlying object / data |
225 | by calling get_object, and work with it directly. |
226 | |
227 | |
228 | =item get_object( ) |
229 | |
230 | This method returns the underlying user object. If your user object is backed |
231 | by another object class, this method should return that underlying object. |
232 | This allows the application developer to obtain an editable object. Generally |
233 | speaking this will only be done by developers who know what they are doing and |
234 | require advanced functionality which is either unforeseen or inconsistent |
235 | across user classes. If your object is not backed by another class, or you |
236 | need to provide additional intermediate functionality, it is perfectly |
237 | reasonable to return C<$self>. |
238 | |
239 | =back |
240 | |
241 | |
4b69b736 |
242 | =head1 WRITING A CREDENTIAL |
243 | |
244 | There are two parts to an authentication store, the backend and the user object. |
245 | |
246 | =head2 STORAGE BACKEND |
649de93b |
247 | ... Documentation fairy fell asleep here. |