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 |
45c7644b |
20 | one credential and one store. A pairing of a store and a credential |
649de93b |
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 |
32dbfc7f |
35 | actual call to the Catalyst application's authenticate method. |
36 | |
37 | A simplified description of the authentication process follows: |
38 | |
39 | |
40 | |
41 | B<Initialization> |
42 | |
43 | =over 4 |
44 | |
45 | B<Realm Setup> - for each realm: |
46 | |
47 | =over 4 |
5cd1efd6 |
48 | |
8a7bd676 |
49 | 1) The Realm is instantiated using new() method |
32dbfc7f |
50 | |
8a7bd676 |
51 | 2) The Store is instantiated using new() method |
32dbfc7f |
52 | |
8a7bd676 |
53 | 3) The Credential Instantiated using new() method |
32dbfc7f |
54 | |
8a7bd676 |
55 | 4) Credential and Store objects tied to realm for use during requests |
32dbfc7f |
56 | |
57 | =back |
58 | |
59 | =back |
60 | |
61 | B<Authentication> |
62 | |
63 | =over 4 |
64 | |
65 | C<< $c->authenticate( $userinfo, $realm ) >> called |
66 | |
67 | =over 4 |
68 | |
8a7bd676 |
69 | 1) Credential object retrieved for realm provided |
32dbfc7f |
70 | |
8a7bd676 |
71 | 2) Credential's authenticate() method called with authinfo and realm object for current realm |
32dbfc7f |
72 | |
73 | =over 4 |
74 | |
8a7bd676 |
75 | The realm object and the authinfo hash are provided to the credential object's |
76 | authenticate call. In most cases the credential object will attempt to |
77 | retrieve a user using the realm's find_user() method, which by default relays |
78 | the call directly to the Store's find_user() method. It will then usually |
79 | compare the retrieved user's information with the information provided in the |
80 | $authinfo hash. This is how the default 'Password' credential functions. If |
81 | the credentials match, the authenticate() method should return a user object. |
32dbfc7f |
82 | |
83 | =back |
84 | |
85 | 3) User object stored in session |
86 | |
87 | =over 4 |
88 | |
8a7bd676 |
89 | If the user object supports session storage, the successfully authenticated |
90 | user will be placed in session storage. This is done by calling the realm |
bf4d93a4 |
91 | object's persist_user() method. The persist_user() routine by |
8a7bd676 |
92 | default calls the Store's for_session() method, which should return serialized |
93 | data (IE a scalar). This serialized data is passed back to the store via the |
94 | from_session() method, so the data should contain enough information for the |
95 | store to recreate / reload the user. |
32dbfc7f |
96 | |
97 | =back |
98 | |
99 | =back |
100 | |
101 | =back |
102 | |
103 | B<Sessions> - Per-Request operations |
104 | |
105 | =over 4 |
106 | |
107 | When any user-related activity occurs, and $c->authenticate has not |
108 | yet been called, the Catalyst::Plugin::Authentication module will |
bf4d93a4 |
109 | attempt to restore the persisted user (normally from the session if one is available). |
32dbfc7f |
110 | There is only one step in this process: |
111 | |
112 | =over 4 |
113 | |
114 | 1) Store object's from_session() is called |
115 | |
116 | =back |
117 | |
118 | The serialized data previously returned by the store's for_session() |
119 | method is provided to the from_session() method. The from_session() |
120 | method should return a valid user object. |
121 | |
122 | Note that the for_session() is only called during the original |
123 | $c->authenticate() call, so if changes are made to the user that need |
124 | to be reflected in your session data, you will want to call the |
bf4d93a4 |
125 | $c->persist_user() method - which will perform the session |
32dbfc7f |
126 | storage process again (complete with call to for_session()). |
127 | |
128 | =back |
129 | |
130 | More detailed information about these processes is below. |
649de93b |
131 | |
132 | =head2 INITIALIZATION |
133 | |
134 | When the authentication module is loaded, it reads it's configuration to |
135 | determine the realms to set up for the application and which realm is to be |
136 | the default. For each realm defined in the application's config, |
137 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> |
138 | instantiates both a new credential object and a new store object. See below |
139 | for the details of how credentials and stores are instantiated. |
140 | |
32dbfc7f |
141 | B<NOTE>: The instances created will remain active throughout the entire |
142 | lifetime of the application, and so should be relatively lightweight. |
143 | Care should be taken to ensure that they do not grow, or retain |
144 | information per request, because they will be involved in each |
145 | authentication request and could therefore substantially |
146 | hurt memory consumption over time. |
649de93b |
147 | |
148 | =head2 AUTHENTICATION |
149 | |
150 | When C<$c-E<gt>authenticate()> is called from within an application, the |
151 | objects created in the initialization process come into play. |
152 | C<$c-E<gt>authenticate()> takes two arguments. The first is a hash reference |
153 | containing all the information available about the user. This will be used to |
154 | locate the user in the store and verify the user's credentials. The second |
155 | argument is the realm to authenticate against. If the second argument is |
156 | omitted, the default realm is assumed. |
157 | |
158 | The main authentication module then locates the credential and store objects |
159 | for the realm specified and calls the credential object's C<authenticate()> |
160 | method. It provides three arguments, first the application object, or C<$c>, |
161 | then a reference to the store object, and finally the hashref provided in the |
162 | C<$c-E<gt>authenticate> call. The main authentication module expects the |
163 | return value to be a reference to a user object upon successful |
164 | authentication. If it receives anything aside from a reference, it is |
165 | considered to be an authentication failure. Upon success, the returned user is |
166 | marked as authenticated and the application can act accordingly, using |
167 | C<$c-E<gt>user> to access the authenticated user, etc. |
168 | |
169 | Astute readers will note that the main |
170 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> module |
171 | does not interact with the store in any way, save for passing a reference to |
172 | it to the credential. This is correct. The credential object is responsible |
173 | for obtaining the user from the provided store using information from the |
174 | userinfo hashref and/or data obtained during the credential verification |
175 | process. |
176 | |
177 | =head1 WRITING A STORE |
178 | |
45c7644b |
179 | There are two parts to an authentication store, the store object and the user object. |
649de93b |
180 | |
181 | =head2 STORAGE BACKEND |
182 | |
45c7644b |
183 | Writing a store is actually quite simple. There are only five methods |
649de93b |
184 | that must be implemented. They are: |
185 | |
186 | new() - instantiates the store object |
187 | find_user() - locates a user using data contained in the hashref |
188 | for_session() - prepares a user to be stored in the session |
189 | from_session() - does any restoration required when obtaining a user from the session |
190 | user_supports() - provides information about what the user object supports |
191 | |
192 | =head3 STORE METHODS |
193 | |
194 | =over 4 |
195 | |
8a7bd676 |
196 | =item new( $config, $app, $realm ) |
649de93b |
197 | |
198 | The C<new()> method is called only once, during the setup process of |
199 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>. The |
200 | first argument, C<$config>, is a hash reference containing the configuration |
201 | information for the store module. The second argument is a reference to the |
202 | Catalyst application. |
203 | |
32dbfc7f |
204 | Note that when new() is called, Catalyst has not yet loaded |
205 | the various controller and model classes, nor is it definite |
206 | that other plugins have been loaded, so your new() method |
207 | must not rely on any of those being present. If any of |
208 | this is required for your store to function, you should |
209 | defer that part of initialization until the first method call. |
4b69b736 |
210 | |
649de93b |
211 | The C<new()> method should return a blessed reference to your store object. |
212 | |
213 | =item find_user( $authinfo, $c ) |
214 | |
215 | This is the workhorse of any authentication store. It's job is to take the |
216 | information provided to it via the C<$authinfo> hashref and locate the user |
217 | that matches it. It should return a reference to a user object. A return value |
218 | of anything else is considered to mean no user was found that matched the |
219 | information provided. |
220 | |
221 | How C<find_user()> accomplishes it's job is entirely up to you, the author, as |
222 | is what $authinfo is required to contain. Many stores will simply use a |
223 | username element in $authinfo to locate the user, but more advanced functionality |
224 | is possible and you may bend the $authinfo to your needs. Be aware, however, that |
c5fbff80 |
225 | both Credentials and Stores usually work with the same $authinfo hash, so take |
226 | care to avoid overlapping element names. |
649de93b |
227 | |
228 | Please note that this routine may be called numerous times in various |
229 | circumstances, and that a successful match for a user here does B<NOT> |
230 | necessarily constitute successful authentication. Your store class should |
231 | never assume this and in most cases C<$c> B<should not be modified> by your |
232 | store object. |
233 | |
234 | =item for_session( $c, $user ) |
235 | |
236 | This method is responsible for preparing a user object for storage in the session. |
237 | It should return information that can be placed in the session and later used to |
238 | restore a user object (using the C<from_session()> method). It should therefore |
239 | ensure that whatever information provided can be used by the C<from_session()> |
240 | method to locate the unique user being saved. Note that there is no guarantee |
241 | that the same Catalyst instance will receive both the C<for_session()> and |
242 | C<from_session()> calls. You should take care to provide information that can |
243 | be used to restore a user, regardless of the current state of the application. |
244 | A good rule of thumb is that if C<from_session()> can revive the user with the |
245 | given information even if the Catalyst application has just started up, you are |
246 | in good shape. |
247 | |
248 | =item from_session( $c, $frozenuser ) |
249 | |
250 | This method is called whenever a user is being restored from the session. |
251 | C<$frozenuser> contains the information that was stored in the session for the user. |
252 | This will under normal circumstances be the exact data your store returned from |
253 | the previous call to C<for_session()>. C<from_session()> should return a valid |
254 | user object. |
255 | |
256 | =item user_supports( $feature, ... ) |
257 | |
258 | This method allows credentials and other objects to inquire as to what the |
259 | underlying user object is capable of. This is pretty-well free-form and the |
260 | main purpose is to allow graceful integration with credentials and |
261 | applications that may provide advanced functionality based on whether the |
262 | underlying user object can do certain things. In most cases you will want to |
263 | pass this directly to the underlying user class' C<supports> method. Note that |
264 | this is used as a B<class> method against the user class and therefore must |
32dbfc7f |
265 | be able to function without an instantiated user object. |
649de93b |
266 | |
267 | =back |
268 | |
30629676 |
269 | =head3 OPTIONAL STORE METHODS |
270 | |
271 | If you want your store to be able to auto- create users, then you can |
272 | implement these methods: |
273 | |
fd9de2d9 |
274 | =head4 auto_update_user( $authinfo, $c, $res ) |
30629676 |
275 | |
276 | This method is called if the realm's auto_update_user setting is true. |
277 | |
fd9de2d9 |
278 | =head4 auto_create_user( $authinfo, $c ) |
30629676 |
279 | |
280 | This method is called if the realm's auto_create_user setting is true. |
281 | |
649de93b |
282 | =head2 USER OBJECT |
283 | |
284 | The user object is an important piece of your store module. It will be the |
285 | part of the system that the application developer will interact with most. As |
286 | such, the API for the user object is very rigid. All user objects B<MUST> |
287 | inherit from |
5c5af345 |
288 | L<Catalyst::Authentication::User|Catalyst::Authentication::User>. |
649de93b |
289 | |
290 | =head3 USER METHODS |
291 | |
292 | The routines required by the |
293 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> plugin |
294 | are below. Note that of these, only get_object is strictly required, as the |
5c5af345 |
295 | L<Catalyst::Authentication::User|Catalyst::Authentication::User> |
649de93b |
296 | base class contains reasonable implementations of the rest. If you do choose |
4b69b736 |
297 | to implement only the C<get_object()> routine, please read the base class code |
298 | and documentation so that you fully understand how the other routines will be |
649de93b |
299 | implemented for you. |
300 | |
301 | Also, your user object can implement whatever additional methods you require |
302 | to provide the functionality you need. So long as the below are implemented, |
303 | and you don't overlap the base class' methods with incompatible routines, you |
304 | should experience no problems. |
305 | |
306 | =over 4 |
307 | |
308 | =item id( ) |
309 | |
310 | The C<id()> method should return a unique id (scalar) that can be used to |
311 | retreive this user from the store. Often this will be provided to the store's |
312 | C<find_user()> routine as C<id =E<gt> $user-E<gt>id> so you should ensure that your |
313 | store's C<find_user()> can cope with that. |
314 | |
32dbfc7f |
315 | =item supports( $feature, $subfeature ... ) |
316 | |
317 | This method checks to see if the user class supports a particular feature. It |
318 | is implemented such that each argument provides a subfeature of the previous |
319 | argument. In other words, passing 'foo', 'bar' would return true if the user |
320 | supported the 'foo' feature, and the 'bar' feature of 'foo'. This is implemented |
5c5af345 |
321 | in Catalyst::Authentication::User, so if your class inherits from that, you |
32dbfc7f |
322 | do not need to implement this and can instead implement supported_features(). |
323 | |
324 | B<Note:> If you want the authentication module to be able to save your user in |
325 | the session you must return true when presented with the feature 'session'. |
326 | |
327 | =item supported_features( ) |
649de93b |
328 | |
32dbfc7f |
329 | This method should return a hashref of features supported by the user class. |
330 | This is for more flexible integration with some Credentials / applications. It |
331 | is not required that you support anything, and returning C<undef> is perfectly |
649de93b |
332 | acceptable and in most cases what you will do. |
333 | |
334 | =item get( $fieldname ) |
335 | |
336 | This method should return the value of the field matching fieldname provided, |
337 | or undef if there is no field matching that fieldname. In most cases this will |
338 | access the underlying storage mechanism for the user data and return the |
339 | information. This is used as a standard method of accessing an authenticated |
340 | user's data, and MUST be implemented by all user objects. |
341 | |
32dbfc7f |
342 | B<Note>: There is no equivalent 'set' method. Each user class is |
343 | likely to vary greatly in how data must be saved and it is |
344 | therefore impractical to try to provide a standard way of |
345 | accomplishing it. When an application developer needs to save |
346 | data, they should obtain the underlying object / data by |
347 | calling get_object, and work with it directly. |
649de93b |
348 | |
349 | |
350 | =item get_object( ) |
351 | |
352 | This method returns the underlying user object. If your user object is backed |
353 | by another object class, this method should return that underlying object. |
354 | This allows the application developer to obtain an editable object. Generally |
355 | speaking this will only be done by developers who know what they are doing and |
356 | require advanced functionality which is either unforeseen or inconsistent |
357 | across user classes. If your object is not backed by another class, or you |
358 | need to provide additional intermediate functionality, it is perfectly |
359 | reasonable to return C<$self>. |
360 | |
361 | =back |
362 | |
363 | |
4b69b736 |
364 | =head1 WRITING A CREDENTIAL |
365 | |
c5fbff80 |
366 | Compared to writing a store, writing a credential is very simple. There is only |
367 | one class to implement, and it consists of only two required routines. They are: |
368 | |
369 | new() - instantiates the credential object |
370 | authenticate() - performs the authentication and returns a user object |
371 | |
372 | =head2 CREDENTIAL METHODS |
373 | |
374 | =over 4 |
375 | |
8a7bd676 |
376 | =item new( $config, $app, $realm ) |
c5fbff80 |
377 | |
378 | Like the Store method of the same name, the C<new()> method is called only |
379 | once, during the setup process of |
380 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>. The |
381 | first argument, C<$config>, is a hash reference containing the configuration |
382 | information for the credential module. The second argument is a reference |
8a7bd676 |
383 | to the Catalyst application. $realm is the instantiated Realm object, which |
384 | you may use to access realm routines - such as find_user. |
c5fbff80 |
385 | |
32dbfc7f |
386 | Again, when the credential's new() method is called, Catalyst |
387 | has not yet loaded the various controller and model classes. |
c5fbff80 |
388 | |
389 | The new method should perform any necessary setup required and instantiate |
390 | your credential object. It should return your instantiated credential. |
391 | |
8a7bd676 |
392 | =item authenticate( $c, $realm, $authinfo ) |
c5fbff80 |
393 | |
394 | This is the workhorse of your credential. When $c->authenticate() is called |
395 | the L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> module retrieves the |
8a7bd676 |
396 | realm object and passes it, along with the $authinfo hash |
c5fbff80 |
397 | to your credential's authenticate method. Your module should use the |
8a7bd676 |
398 | $authinfo hash to obtain the user from the realm passed, and then perform |
c5fbff80 |
399 | any credential verification steps necessary to authenticate the user. This |
400 | method should return the user object returned by the authentication store if |
401 | credential verification succeeded. It should return undef on failure. |
402 | |
403 | How your credential module performs the credential verification is entirely |
404 | up to you. In most cases, the credential will retrieve a user from the store |
405 | first (using the stores find_user() method), and then validate the user's |
406 | information. However, this does not have to be the case. |
407 | |
408 | It is perfectly acceptable for your credential to perform other tasks prior to |
409 | attempting to retrieve the user from the store. It may also make sense for |
410 | your credential to perform activities which help to locate the user in |
411 | question, for example, finding a user id based on an encrypted token. |
8a7bd676 |
412 | In these scenarios, the $authinfo hash passed to find_user() |
c5fbff80 |
413 | can be different than that which is passed in to $c->authenticate(). Once |
414 | again this is perfectly acceptable if it makes sense for your credential, |
415 | though you are strongly advised to note this behavior clearly in your |
416 | credential's documentation - as application authors are almost |
417 | certainly expecting the user to be found using the information provided |
418 | to $c->authenticate(). |
419 | |
5c5af345 |
420 | Look at the L<Catalyst::Authentication::Credential::Password|Catalyst::Authentication::Credential::Password> |
c5fbff80 |
421 | module source to see this in action. In order to avoid possible |
422 | mismatches between the encrypted and unencrypted passwords, the password |
423 | credential actually removes the provided password from the authinfo |
424 | array. It does this because, in many cases, the store's password |
425 | field will be encrypted in some way, and the password passed to |
426 | $c->authenticate is almost certainly in plaintext. |
427 | |
428 | NOTE: You should always assume that a store is going to use all |
429 | the information passed to it to locate the user in question. |
430 | If there are fields in the $authinfo hash that you are sure |
431 | are specific to your credential, you may want to consider |
432 | removing them before user retrieval. A better solution is to |
433 | place those arguments that are specific to your credential |
434 | within their own subhash named after your module. |
435 | |
5c5af345 |
436 | The L<Catalyst::Authentication::Store::DBIx::Class|Catalyst::Authentication::Store::DBIx::Class> module does this |
c5fbff80 |
437 | in order to encapsulate arguments intended specifically for |
5c5af345 |
438 | that module. See the L<Catalyst::Authentication::Store::DBIx::Class::User|Catalyst::Authentication::Store::DBIx::Class::User> |
c5fbff80 |
439 | source for details. |
440 | |
441 | =back |
442 | |
443 | =head1 AUTHORS |
444 | |
445 | Jay Kuri, C<jayk@cpan.org> |
446 | |
447 | =head1 COPYRIGHT & LICENSE |
448 | |
d6209239 |
449 | Copyright (c) 2005 the aforementioned authors. All rights |
450 | reserved. This program is free software; you can redistribute |
451 | it and/or modify it under the same terms as Perl itself. |
c5fbff80 |
452 | |
453 | =cut |
454 | |