# -*- fundamental -*- =head1 NAME CGI::Auth::Flexible - web authentication optionally using cookies =head1 SYNOPSYS my $verifier = CGI::Auth::Flexible->new_verifier(setting => value,...); my $authreq = $verifier->new_request($cgi); # simple applications $authreq->check_ok() or return; # sophisticated applications my $divert_kind = $authreq->check_divert(); if ($divert_kind) { ... print diversion page and quit ... } # while handling the request $user = $authreq->get_username(); $authreq->check_mutate(); =head1 DESCRIPTION CGI::Auth::Flexible is a library which you can use to add a forms/cookie-based login facility to a Perl web application. CGI::Auth::Flexible doesn't interfere with your application's URL path namespace and just needs a few (configurable) form parameter and cookie name(s) for its own use. It tries to avoid making assumptions about the implementation structure of your application. Because CGI::Auth::Flexible is licenced under the AGPLv3, you will probably need to provide a facility to allow users (even ones not logged in) to download the source code for your web app. Conveniently by default CGI::Auth::Flexible provides (for pure Perl webapps) a mechanism for users to get the source. CGI::Auth::Flexible is designed to try to stop you accidentally granting access by misunderstanding the API. (Also it, of course, guards against cross-site scripting.) You do need to make sure to call CGI::Auth::Flexible before answering AJAX requests as well as before generating HTML pages, of course, and to call it in every entrypoint to your system. =head2 CHECKLIST As a minimum you need to do all of the things on this checklist, where applicable. The items marked SECURITY are the ones that you might forget: without them your application may appear to work, but will be insecure. =over =item * Call C (once at application startup) =item * Call C (once per request) =item * B: Call C or C on every request, and honour the return value. =item * If you're using C, implement either the C or C hook and provide it as a setting to C. =item * Provide the setting C (or provide absolute paths for all the other relevant settings). =item * Call C when you need to know who's logged in. =item * B: Call C or C, if you specified C. =item * B: Call C for every request which is not a page load (if your application has any of those). =item * When generating URLs and forms (including AJAX requests), include the hidden form parameter using C or C when appropriate (see below). =item * B: If you do not override the source provision facility (see L), check that the assumptions it makes aren't going to leak security-critical data. =item * Set up HTTPS on your webserver, or set the C setting to a false value. =back These points will now be covered in more detail. =head2 INITIALISATION Your application should, on startup (eg, when it is loaded by mod_perl) do C<< $verifier = CGI::Auth::Flexible->new_verifier(settings...) >>. This call can be expensive and is best amortised. The resulting verifier object can be used to process individual requests, in each case with C<< $authreq = CGI::Auth::Flexible->new_request($cgi) >>. See L. =head2 CHECKING AND RESPONSE GENERATION If the user is logged in, your application is to handle the request. Otherwise, the user needs to be presented with a login form or error message, as appropriate. CGI::Auth::Flexible provides two alternative interfaces for this: =head3 Simple applications The simplist usage is to call C<< $request->check_ok() >> which will check the user's authentication. If the user is not logged in it will generate a login form (or redirection or other appropriate page) and return false; your application should not then processing that request any further. If the user is logged in it will return true. Various hooks are provided to customise the responses generated by C. After C returns true you should go ahead and process the request; you can use C<< $request->get_username >> to find out which user the request came from. =head2 Sophisticated applications If you want to handle the control flow and to generate login forms, redirections, etc., yourself, you can say C<< $divert = $request->check_divert >>. This returns undef if the user is logged in, or I if some kind of login page or diversion should be generated. See L below for details of how to deal with the return value. =head2 MUTATING OPERATIONS AND EXTERNAL LINKS =head3 Mutation-ignorant applications By default CGI::Auth::Flexible does not permit external links into your site. All GET requests give a "click to continue" page which submits a form which loads your app's main page. In this configuration all your application's forms and AJAX requests should use C. This restriction arises from complicated deficiencies in the web's security architecture. Such applications are also not able to provide user-specific CSS stylesheets, javascript, favicons, etc. =head3 Mutation-aware applications The alternative is for your application to always make a special check when the incoming request is going to do some kind of action (such as modifying the user's setup, purchasing goods, or whatever) rather than just retrieve and/or display information. We term such requests "mutating" requests. Then non-mutating pages can be linked to from other, untrustworthy, websites. To support external links, and C requests, pass C<< promise_check_mutate => 1 >> in I, and then call C<< $authreq->check_mutate() >> before taking any actions. If the incoming request is not suitable then C<< $authreq->check_mutate() >> will call C. There have to be no mutating C requests in your application (but you shouldn't have any of those anyway); if there are, they won't work. (CGI::Auth::Flexible will spot them and cause them to fail, rather than allow them to be insecure.) =head2 GENERATING URLS, FORMS AND AJAX QUERIES When you generate a URL, C form or AJAX request you may need to include a secret hidden form parameter for the benefit of CGI::Auth::Generic. This form parameter will be checked by C/C and should be ignored by your application. By default the hidden parameter is called C. After calling C or C the value to put in your form can be obtained from C; C will generate the whole HTML C<< >> element. =head3 Mutation-ignorant applications For mutation-ignorant applications (see above), all forms etc. should include the hidden parameter (and as discussed, they must all use POST rather than GET). =head3 Mutation-aware applications For mutation-aware applications, whether to include the secret parameter depends on the kind of request. CGI::Auth::Flexible knows when it is necessary. You should find out by calling C. If it is inconvenient to call C at runtime, you can rely instead on the following promises: All POST requests (which includes all mutating requests) need the parameter. The return value of need_add_hidden depends only on the $method and $reqtype parameters, so you can query it once and remember the answer. HTML page load GETs do not need the parameter. It is better to err on the side of including the parameter. If you really must, you can call C "on the bench" during development and bake the answer into your application code structure. However, if you do that and a new vulnerability was discovered which is fixed by changing the answer, updating CGI::Auth::Flexible wouldn't be sufficient to fix it. =head3 Mutation-aware applications - non-page requests If your mutation-aware application supports non-page resources (AJAX and JSON requests, stylesheets, favicons, etc.) it must inform CGI::Auth::Flexible when it is handling such a request, by calling C. Normally C will simply return (and you can ignore the return value). However, if there is an attack (or, perhaps, a bug) it will die, stopping the attack. (You do not need to call C for POST requests, but it is harmless to do so.) =head3 Mutation-aware applications - novel kinds of request If you want to support a kind of request perhaps not yet known about by CGI::Auth::Flexible, you can provide information about that new request kind using C. =head2 DATA STORAGE CGI::Auth::Flexible needs to store various information in plain files; it does this in the directory specified by the C parameter. =head1 SOURCE CODE DOWNLOAD By default, CGI::Auth::Flexible provides a facility for users to download the source code for the running version of your web application. This facility makes a number of important assumptions which you need to check. Note that if the provided facility is not sufficient because your application is more sophisticated than it copes with (or if you disable the builtin facility), you may need to implement a functioning alternative to avoid violating the AGPLv3 licence. Here are the most important (default) assumptions: =over =item * Your app's source code is available by looking at @INC, $0 and S<$ENV{'SCRIPT_FILENAME'}> (the B). See C. Where these point to files or directories under revision control, the source item is the whole containing vcs tree. =item * Specifically, there are no compiled or autogenerated Perl files, Javascript resources, etc., which are not contained in one of the source item directories. (Files which came with your operating system install don't need to be shipped as they fall under the system library exception.) =item * You have not installed any modified versions of system libraries (including system-supplied Perl modules) in C outside C. See C. =item * For each source item in a dvcs, the entire dvcs history does not contain anything confidential (or libellous). Also, all files which contain secrets are in the dvcs's I<.ignore> file. See C et al. =item * For each source item NOT in a dvcs, there are no confidential files with the world-readable bit set (being in a world-inaccessible directory is not sufficient). See C. =item * You have none of your app's source code in C. =item * You don't regard pathnames on your server as secret. =item * You don't intentionally load Perl code by virtue of C<.> being in C<@INC> by default. (See C.) =back =head1 MAIN FUNCTIONS AND METHODS =over =item C<< CGI::Auth::Flexible->new_verifier(setting => value, ...) >> Initialises an instance and returns a verifier object. The arguments are setting pairs like a hash initialiser. See L below. =item C<< $verifier->new_request($cgi) >> Prepares to process a request. I<$cgi> should normally be the query object from L. Most of the default hook methods assume that it is; however if you replace enough of the hook methods then you can pass any value you like and it will be passed to your hooks. The return value is the authentication request object (I<$authreq>) which is used to check the incoming request and will contain information about its credentials. =item C<< $authreq->check_divert() >> Checks whether the user is logged in. Returns undef if the user is logged in and we should service the request. Otherwise returns a divert spec (see L) saying what should happen instead. This method may die if it doesn't like the request, in which case the request needs to be rejected. =item C<< $authreq->check_ok() >> Checks whether the user is logged in. Returns true if the user is logged in and we should service the request. Otherwise it handles the request itself, generating any appropriate redirect, login form, or continuation page. It then returns false and the application should not process the request further. =item C<< $verifier->disconnect() >> Discards the resources (open files, etc.) in the verifier object. =back =head1 REQUEST-RELATED FUNCTIONS AND METHODS All of these are only valid after C or C has been called. (In the case of C it won't normally be sensible to call these functions unless C returned true.) =item C<< $authreq->get_divert() >> Returns the value previously returned by C. =item C<< $authreq->get_username() >> Returns the name of the logged-in user. If the user was not logged in (or their session had timed out, or something), returns undef. =item C<< $authreq->check_mutate() >> Declares to CGI::Auth::Generic that the request being handled will "mutate". That is, it will modify some server-side state (eg, adding items to shopping baskets, posting messages to blogs, sending emails, or whatever). If you have set the setting C you must call C whenever appropriate. If you haven't then it's irrelevant. See L. C will either return successfully, indicating that all is well and the request should proceed, or it will die. If it dies that means that the request was improper, which can only result from a bug or an attack. So an "internal server error" is a suitable response. =item C<< $authreq->check_nonpage($method, $reqtype) >> Declares to CGI::Auth::Generic that the request is not a page request, but rather a request of type I<$reqtype>. If your application has set the setting C, whenever it is handling anything except an HTML page loads, it must call this function. See L, and L. C will either return successfully, indicating that all is well and the request should proceed, or it will die, like C. =head1 RESPONSE-RELATED FUNCTIONS AND METHODS =item C<< $authreq->url_with_query_params($params, [$nonpagetype]) >> Convenience function which returns a url for a GET request to this application. I<$params> is a hashref specifying the parameters and the PATH_INFO. The keys are the parameter names, and the values are array refs with the parameter value(s) (as strings, as yet unquoted). (They are array refs because it is possible to pass multiple values for the same parameter in a single request; normally each arrayref would be a singleton.) The request path will be the path to the application. If a parameter with name C<< '' >> is supplied, it is taken as the PATH_INFO - its value will be appended to the application path. (It should normally start with C<< / >>, and only one value should be supplied.) =item C<< something->need_add_hidden($method, $reqtype) >> Enquires whether a request of type I<$reqtype> using HTTP method I<$method> needs the hidden form parameter. See L. =item C<< something->secret_hidden_val() >> Returns the value of the hidden form parameter. This should be included in all POST requests to your application (and thus be a hidden form parameter in all forms). It should also be in some (maybe all) GET requests. If your application is mutation-ignorant, it should be in all GET requests. If you are mutation-aware, you need to consult C. The name of the hidden parameter is the setting C, C by default. xxx rename param and setting =item C<< something->secret_hidden_html() >> Returns the HTML for an C element specifying the hidden form parameter. =item C<< something->secret_cookie_val() >> Returns the value of the secret cookie. CGI::Auth::Flexible sets this cookie in the forms generated by C. You may also set it yourself (and indeed you must do so if you use C). =back =head1 OTHER FUNCTIONS AND METHODS =over =item C<< $verifier_or_authreq->hash($data) >> Hashes the supplied data using the hash function specified by the C setting, and converts the result to a string of hex digits. =item C<< something->update_get_need_add_hidden($reqtype, $value, [$force]) >> Updates CGI::Auth::Generic's knowledge about the various kinds of request, and whether they need the hidden form parameter. This function applies only to GET requests - POST requests always use the parameter. I<$reqtype> is the request type (the value which will be passed to C and C. If you are supporting a new I<$reqtype> you shouuld coordinate with CGI::Auth::Flexible upstrea, or other users, to assign a unique request type name. This method may be called on an authreq or a verifier, in which case it will affect all authreqs using the same verifier. Or it may be called on the class as a whole, in which case it will affect the global default list for all verifiers. If I<$force> is supplied and true, this will override CGI::Auth::Flexible's existing knowledge. Otherwise this new setting will be ignored if CGI::Auth::Flexible already knows about the request type. (When called on a verifier or authreq, it will ignore the update in favour of existing knowledge recorded both globally in the class or due to previous requests on the same verifier.) See L. =item C<< CGI::Auth::Flexible::srcdump_dir_cpio($cgi,$verifier,$dumpdir,$dir,$outfn,$how,$script) >> Helper function for implementing the C hook. Generates a tarball using cpio and includes it in the prepared source code distribution. The arguments are mostly the same as for that hook. C<$dir> is the root directory at which to start the archive. C<$how> is a short text string which will be mentioned in the log. C<$script> is a shell script fragment which must output a nul-separated list of filenames (e.g. the output of C). It is textually surrounded by C<( )> and will be executed with C in force. Its cwd will be C<$dir>. =item C<< $verifier_or_authreq->($data) | CGI::Auth::Flexible-> >> Hashes the supplied data using the hash function specified by the C setting, and converts the result to a string of hex digits. =back =head1 REQUEST TYPES The C<$reqtype> values understood by C are strings. They are: =over =item C A top-level HTML page load. May contain confidential information for the benefit of the logged-in user. =item C An HTML frame. May contain confidential information for the benefit of the logged-in user. =item C