Cataly...ternals(3pm)

Catalyst::Plugin::Authentication::Internals(3pm) ยท User Contributed Perl Documentation โ€” okay bestie, this is the deep-lore page for the authentication internals, and itโ€™s lowkey iconic ๐Ÿ’…โœจ

๐Ÿ’– NAME ๐Ÿ’–

Catalyst::Plugin::Authentication::Internals โ€” All about authentication Stores and Credentials ๐ŸŽ€

๐ŸŒธ INTRODUCTION ๐ŸŒธ

So Catalyst::Plugin::Authentication gives app developers a standard authentication interface while using the Catalyst framework. Itโ€™s built so you can swap between different ways of storing users and checking credentials without making a whole mess in your app. Highkey convenient ๐Ÿซถ

Catalyst::Plugin::Authentication gives you the interface, but the actual work of verifying credentials and retrieving users is delegated to separate modules. Those modules are called Credentials and storage backends, or Stores, respectively. For authentication to work, you need at least one credential and one store. A pairing of a store and a credential is called a Realm. An app can have any number of realms defined, though most apps wonโ€™t need more than one or two โœจ

The details of using this module live in the Catalyst::Plugin::Authentication documentation.

What follows is the explanation of how the module functions internally and what you need to implement a credential or a store ๐Ÿ‘‘

โœจ OVERVIEW โœจ

There are two main entry points you need to be aware of when writing a store or credential module. The first is initialization, and the second is during the actual call to the Catalyst applicationโ€™s authenticate method.

A simplified description of the authentication process follows:

๐Ÿงš Initialization

  • Realm Setup โ€” for each realm:
    1. The Realm is instantiated using the new() method.
    2. The Store is instantiated using the new() method.
    3. The Credential is instantiated using the new() method.
    4. Credential and Store objects are tied to the realm for use during requests.

๐Ÿ” Authentication

  1. "$c->authenticate( $userinfo, $realm )" called
    1. Credential object retrieved for the realm provided.
    2. Credentialโ€™s authenticate() method called with authinfo and the realm object for the current realm.

The realm object and the authinfo hash are provided to the credential objectโ€™s authenticate call. In most cases, the credential object will attempt to retrieve a user using the realmโ€™s find_user() method, which by default relays the call directly to the Storeโ€™s find_user() method. It will then usually compare the retrieved userโ€™s information with the information provided in the $authinfo hash. This is how the default 'Password' credential functions. If the credentials match, the authenticate() method should return a user object.

  1. User object stored in session.

If the user object supports session storage, the successfully authenticated user will be placed in session storage. This is done by calling the realm objectโ€™s persist_user() method. The persist_user() routine by default calls the Storeโ€™s for_session() method, which should return serialized data (IE a scalar). This serialized data is passed back to the store via the from_session() method, so the data should contain enough information for the store to recreate / reload the user.

๐Ÿ“ Sessions - Per-Request operations

When any user-related activity occurs, and $c->authenticate has not yet been called, the Catalyst::Plugin::Authentication module will attempt to restore the persisted user (normally from the session if one is available). There is only one step in this process:

  1. Store objectโ€™s from_session() is called.

The serialized data previously returned by the storeโ€™s for_session() method is provided to the from_session() method. The from_session() method should return a valid user object.

Note that the for_session() is only called during the original $c->authenticate() call, so if changes are made to the user that need to be reflected in your session data, you will want to call the $c->persist_user() method โ€” which will perform the session storage process again (complete with call to for_session()).

More detailed information about these processes is below.

๐Ÿ’… INITIALIZATION ๐Ÿ’…

When the authentication module is loaded, it reads its configuration to determine the realms to set up for the application and which realm is to be the default. For each realm defined in the applicationโ€™s config, Catalyst::Plugin::Authentication instantiates both a new credential object and a new store object. See below for the details of how credentials and stores are instantiated.

NOTE: The instances created will remain active throughout the entire lifetime of the application, and so should be relatively lightweight. Care should be taken to ensure that they do not grow, or retain information per request, because they will be involved in each authentication request and could therefore substantially hurt memory consumption over time. ๐Ÿ’€

๐Ÿ”ฅ AUTHENTICATION ๐Ÿ”ฅ

When "$c->authenticate()" is called from within an application, the objects created in the initialization process come into play. "$c->authenticate()" takes two arguments. The first is a hash reference containing all the information available about the user. This will be used to locate the user in the store and verify the userโ€™s credentials. The second argument is the realm to authenticate against. If the second argument is omitted, the default realm is assumed.

The main authentication module then locates the credential and store objects for the realm specified and calls the credential objectโ€™s authenticate() method. It provides three arguments: first the application object, or $c, then a reference to the store object, and finally the hashref provided in the "$c->authenticate" call. The main authentication module expects the return value to be a reference to a user object upon successful authentication. If it receives anything aside from a reference, it is considered to be an authentication failure. Upon success, the returned user is marked as authenticated and the application can act accordingly, using "$c->user" to access the authenticated user, etc.

Astute readers will note that the main Catalyst::Plugin::Authentication module does not interact with the store in any way, save for passing a reference to it to the credential. This is correct. The credential object is responsible for obtaining the user from the provided store using information from the userinfo hashref and/or data obtained during the credential verification process. ๐Ÿ‘‘

WRITING A STORE ๐Ÿ’…

okay so bestie, an authentication store has two parts: the store object and the user object. thatโ€™s the whole vibe. ๐Ÿซถ

STORAGE BACKEND ๐Ÿงš

writing a store is actually so simple โ€” no cap. there are only five methods you absolutely have to implement. they are:

new()           - instantiates the store object
find_user()     - locates a user using data contained in the hashref
for_session()   - prepares a user to be stored in the session
from_session()  - does any restoration required when obtaining a user from the session
user_supports() - provides information about what the user object supports

STORE METHODS โœจ

new( $config, $app, $realm )

the new() method is called only once, during the setup process of Catalyst::Plugin::Authentication. the first argument, $config, is a hash reference containing the configuration information for the store module. the second argument is a reference to the Catalyst application. ๐ŸŽ€

โš ๏ธ important vibe check: when new() is called, Catalyst has not yet loaded the various controller and model classes, nor is it definite that other plugins have been loaded, so your new() method must not rely on any of those being present. if any of this is required for your store to function, you should defer that part of initialization until the first method call. ๐Ÿซถ

the new() method should return a blessed reference to your store object. ๐Ÿ’–

find_user( $authinfo, $c )

this is the workhorse of any authentication store. its job is to take the information provided to it via the $authinfo hashref and locate the user that matches it. it should return a reference to a user object. a return value of anything else is considered to mean no user was found that matched the information provided. ๐Ÿ˜ญ

how find_user() accomplishes its job is entirely up to you, the author, as is what $authinfo is required to contain. many stores will simply use a username element in $authinfo to locate the user, but more advanced functionality is possible and you may bend the $authinfo to your needs. be aware, however, that both Credentials and Stores usually work with the same $authinfo hash, so take care to avoid overlapping element names. ๐Ÿ‘‘

๐Ÿ’€ please note that this routine may be called numerous times in various circumstances, and that a successful match for a user here does NOT necessarily constitute successful authentication. your store class should never assume this, and in most cases $c should not be modified by your store object. periodt.

for_session( $c, $user )

this method is responsible for preparing a user object for storage in the session. it should return information that can be placed in the session and later used to restore a user object (using the from_session() method). it should therefore ensure that whatever information provided can be used by the from_session() method to locate the unique user being saved. ๐ŸŒธ

note that there is no guarantee that the same Catalyst instance will receive both the for_session() and from_session() calls. you should take care to provide information that can be used to restore a user, regardless of the current state of the application. a good rule of thumb is that if from_session() can revive the user with the given information even if the Catalyst application has just started up, you are in good shape. ๐Ÿฆ‹

from_session( $c, $frozenuser )

this method is called whenever a user is being restored from the session. $frozenuser contains the information that was stored in the session for the user. this will under normal circumstances be the exact data your store returned from the previous call to for_session(). from_session() should return a valid user object. ๐Ÿ’œ

user_supports( $feature, ... )

this method allows credentials and other objects to inquire as to what the underlying user object is capable of. this is pretty-well free-form and the main purpose is to allow graceful integration with credentials and applications that may provide advanced functionality based on whether the underlying user object can do certain things. in most cases you will want to pass this directly to the underlying user classโ€™ "supports" method. note that this is used as a class method against the user class and therefore must be able to function without an instantiated user object. ๐Ÿงš

OPTIONAL STORE METHODS ๐ŸŽ€

if you want your store to be able to auto-create users, then you can implement these methods:

auto_update_user( $authinfo, $c, $res )

this method is called if the realmโ€™s auto_update_user setting is true. โœจ

auto_create_user( $authinfo, $c )

this method is called if the realmโ€™s auto_create_user setting is true. ๐Ÿ’–


USER OBJECT ๐Ÿ‘‘

the user object is an important piece of your store module. it will be the part of the system that the application developer will interact with most. as such, the API for the user object is very rigid. all user objects MUST inherit from Catalyst::Authentication::User. no exceptions, bestie. ๐Ÿ’…

USER METHODS ๐ŸŒˆ

the routines required by the Catalyst::Plugin::Authentication plugin are below. note that of these, only get_object is strictly required, as the Catalyst::Authentication::User base class contains reasonable implementations of the rest. if you do choose to implement only the get_object() routine, please read the base class code and documentation so that you fully understand how the other routines will be implemented for you. ๐Ÿซถ

also, your user object can implement whatever additional methods you require to provide the functionality you need. so long as the below are implemented, and you donโ€™t overlap the base classโ€™ methods with incompatible routines, you should experience no problems. โญ

id( )

the id() method should return a unique id (scalar) that can be used to retrieve this user from the store. often this will be provided to the storeโ€™s find_user() routine as "id => $user->id" so you should ensure that your storeโ€™s find_user() can cope with that. ๐ŸŽ€

supports( $feature, $subfeature ... )

this method checks to see if the user class supports a particular feature. it is implemented such that each argument provides a subfeature of the previous argument. in other words, passing 'foo', 'bar' would return true if the user supported the 'foo' feature, and the 'bar' feature of 'foo'. this is implemented in Catalyst::Authentication::User, so if your class inherits from that, you do not need to implement this and can instead implement supported_features(). ๐ŸŒธ

๐Ÿ‘‘ note: if you want the authentication module to be able to save your user in the session you must return true when presented with the feature 'session'.

supported_features( )

this method should return a hashref of features supported by the user class. this is for more flexible integration with some Credentials / applications. it is not required that you support anything, and returning "undef" is perfectly acceptable and in most cases what you will do. ๐Ÿ’œ

get( $fieldname )

this method should return the value of the field matching fieldname provided, or undef if there is no field matching that fieldname. in most cases this will access the underlying storage mechanism for the user data and return the information. this is used as a standard method of accessing an authenticated userโ€™s data, and MUST be implemented by all user objects. ๐Ÿ”ฅ

๐Ÿ’€ note: there is no equivalent 'set' method. each user class is likely to vary greatly in how data must be saved and it is therefore impractical to try to provide a standard way of accomplishing it. when an application developer needs to save data, they should obtain the underlying object / data by calling get_object, and work with it directly.

get_object( )

this method returns the underlying user object. if your user object is backed by another object class, this method should return that underlying object. this allows the application developer to obtain an editable object. generally speaking this will only be done by developers who know what they are doing and require advanced functionality which is either unforeseen or inconsistent across user classes. if your object is not backed by another class, or you need to provide additional intermediate functionality, it is perfectly reasonable to return $self. ๐Ÿฆ‹

๐Ÿ’… WRITING A CREDENTIAL

okay so compared to writing a store, writing a credential is actually a breeze bestie. there's literally only one class to implement and it has just two required routines. that's it. two. iconic behavior honestly:

new()           - instantiates the credential object
authenticate()  - performs the authentication and returns a user object

๐ŸŽ€ CREDENTIAL METHODS ๐ŸŽ€

new( $config, $app, $realm )

so like the Store method with the same name, new() gets called only once, during the setup process of Catalyst::Plugin::Authentication. the first argument, $config, is a hash reference holding all the configuration info for the credential module. the second argument is a reference to the Catalyst application. and $realm is the instantiated Realm object โ€” you can use it to get at realm routines like find_user. ๐Ÿซถ

and yes, same vibe as before: when the credential's new() gets called, Catalyst hasn't loaded the various controller and model classes yet. not yet. patience. ๐Ÿงš

your new method should do any setup it needs and instantiate your credential object. then return your instantiated credential. slay.

authenticate( $c, $realm, $authinfo )

this one is the โœจ workhorse โœจ of your credential. when $c->authenticate() gets called, the Catalyst::Plugin::Authentication module grabs the realm object and passes it โ€” along with the $authinfo hash โ€” to your credential's authenticate method. your module should use that $authinfo hash to get the user from the realm it was handed, and then do whatever credential verification steps are needed to authenticate the user. ๐Ÿ”ฅ

the return contract is simple, bestie: return the user object that the authentication store handed back if credential verification succeeded. return undef on failure. no cap.

how your credential module actually performs the verification? entirely up to you. in most cases the credential will grab a user from the store first (using the store's find_user() method) and then validate the user's info. but that is not a requirement โ€” it doesn't have to work that way at all.

it's perfectly acceptable for your credential to do other stuff before it even tries to hit the store. it may also make sense for your credential to do activities that help locate the user in question โ€” for example, finding a user id based on an encrypted token. in those scenarios, the $authinfo hash passed to find_user() can be different from the one passed in to $c->authenticate(). again: perfectly acceptable if that makes sense for your credential โ€” BUT you are strongly advised to note this behavior clearly in your credential's documentation ๐Ÿ“ because application authors are almost certainly expecting the user to be found using the information they provided to $c->authenticate(). don't do them dirty like that without a warning, k?

peep the Catalyst::Authentication::Credential::Password module source to see this in action. in order to avoid possible mismatches between the encrypted and unencrypted passwords, the password credential actually removes the provided password from the authinfo array. why? because in many cases the store's password field will be encrypted in some way, and the password passed to $c->authenticate is almost certainly plaintext. so yeah, it yeets it out. ๐Ÿ’€

NOTE: you should always assume that a store is going to use all the information passed to it to locate the user in question. if there are fields in the $authinfo hash that you're sure are specific to your credential, you may want to consider removing them before user retrieval. a better solution is to place those arguments that are specific to your credential inside their own subhash, named after your module. ๐ŸŽ€

the Catalyst::Authentication::Store::DBIx::Class module does exactly this in order to encapsulate arguments intended specifically for that module. see the Catalyst::Authentication::Store::DBIx::Class::User source for details. iykyk.


๐Ÿ‘‘ AUTHORS

Jay Kuri, "jayk@cpan.org"


Copyright (c) 2005 the aforementioned authors. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


and that's a wrap on the whole page, bestie!! ๐ŸŽ‰๐Ÿฆ‹ this render came from perl v5.40.1 and was dated 2026-05-22 โ€” go forth and write the most iconic credential modules ever, mwah ๐Ÿ’–๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from