CGI::Ex::Auth - Handle logins nicely.
### authorize the user my $auth = $self->auth({ hook_get_pass_by_user => \&get_pass_by_user, hook_print => \&my_print, login_type => 'sha1', }); ### login_type may be sha1, md5, or plaintext sub get_pass_by_user { my $auth = shift; my $username = shift; my $host = shift; my $password = some_way_of_getting_password; return $password; } sub my_print { my $auth = shift; my $step = shift; my $form = shift; # form includes login_script at this point my $content = get_content_from_somewhere; $auth->cgix->swap_template(\$content, $form); $auth->cgix->print_content_type; print $content; }
CGI::Ex::Auth allows for autoexpiring, safe logins. Auth uses javascript modules that perform SHA1 and MD5 encoding to encode the password on the client side before passing them through the internet.
If SHA1 is used the storage of the password can be described by the following code:
my $pass = "plaintextpassword"; my $save = ($save_the_password) ? 1 : 0; my $time = time; my $store = sha1_hex("$time/$save/" . sha1_hex($pass));
This allows for passwords to be stored as sha1 in a database. Passwords stored in the database this way are still susceptible to bruteforce attack, but are much more secure than storing plain text.
If MD5 is used, the above procedure is replaced with md5_hex.
A downside to this module is that it does not use a session to preserve state so authentication has to happen on every request. A plus is that you don't need to use a session. With later releases, a method will be added to allow authentication to look inside of a stored session somewhat similar to CGI::Session::Auth.
new
Constructor. Takes a hash or hashref of properties as arguments.
init
Called automatically near the end of new.
require_auth
Performs the core logic. Returns true on successful login. Returns false on failed login. If a false value is returned, execution of the CGI should be halted. require_auth WILL NOT automatically stop execution.
$auth->require_auth || exit;
hook_print
Called if login failed. Defaults to printing a very basic page. You will want to override it with a template from your own system. The hook that is called will be passed the step to print (currently only "get_login_info" and "no_cookies"), and a hash containing the form variables as well as the following:
payload - $self->payload error - The error that occurred (if any) key_user - $self->key_user; key_pass - $self->key_pass; key_save - $self->key_save; key_redirect - $self->key_redirect; form_name - $self->form_name; script_name - $ENV{SCRIPT_NAME} path_info - $ENV{PATH_INFO} || '' login_script - $self->login_script($FORM); # The javascript that does the login
success
Method called on successful login. Sets $self->user as well as $ENV{REMOTE_USER}.
user
Returns the user that was successfully logged in (undef if no success).
hook_success
Called from success. May be overridden or a subref may be given as a property.
key_logout
If a key is passed the form hash that matches this key, the current user will be logged out. Default is "logout".
key_cookie
The name of the auth cookie. Default is "ce_auth".
key_cookie_check
A field name used during a bounce to see if cookies exist. Default is "ccheck".
key_user
The form field name used to pass the username. Default is "ce_user".
key_pass
The form field name used to pass the password. Default is "ce_pass".
key_save
The form field name used to pass whether they would like to save the cookie for a longer period of time. Default is "ce_save". The value of this form field should be 1 or 0. If it is zero, the cookie installed will be a session cookie and will expire in $EXPIRE_LOGINS seconds (default of 6 hours).
form_name
The name of the html login form to attach the javascript to. Default is "ce_form".
payload
Additional variables to store in the cookie. Can be used for anything. Should be kept small. Default is time (should always use time as the first argument). Used for autoexpiring the cookie and to prevent bruteforce attacks.
verify_userpass
Called to verify the passed form information or the stored cookie. Calls hook_verify_userpass.
hook_verify_userpass
Called by verify_userpass. Arguments are the username, cookie or info to be tested, and the hostname. Default method calls hook_get_pass_by_user to get the real password. Then based upon how the real password is stored (sha1, md5, plaintext, or crypted) and how the login info was passed from the html form (or javascript), will attempt to compare the two and return success or failure. It should be noted that if the javascript method used is SHA1 and the password is stored crypted or md5'ed - the comparison will not work and the login will fail. SHA1 logins require either plaintext password or sha1 stored passwords. MD5 logins require either plaintext password or md5 stored passwords. Plaintext logins allow for SHA1 or MD5 or crypted or plaintext storage - but should be discouraged because they are plaintext and the users password can be discovered.
hook_get_pass_by_user
Called by hook_verify_userpass. Arguments are the username and hostname. Should return a sha1 password, md5 password, plaintext password, or crypted password depending upon which system is being used to get the information from the user.
set_hook_get_pass_by_user
Allows for setting the subref used by hook_get_pass_by_user.x
cgix
Returns a CGI::Ex object.
form
A hash of passed form info. Defaults to CGI::Ex::get_form.
cookies
The current cookies. Defaults to CGI::Ex::get_cookies.
host
What host are we on. Defaults to a cleaned $ENV{HTTP_HOST}.
basic_login_page
Calls the basic_login_template, swaps in the form variables (including form name, login_script, etc). Then prints content_type, the content, and returns.
basic_login_template
Returns a bare essentials form that will handle the login. Has place holders for all of the form name, and login variables, and errors and login javascript. Variable place holders are of the form [% login_script %] which should work with Template::Toolkit or CGI::Ex::swap_template.
login_type
Either sha1, md5, or plaintext. If global $USE_PLAINTEXT is set, plaintext password will be used. login_type will then look for Digest::SHA1, then Digest::MD5, and then fail to plaintext.
SHA1 comparison will work with passwords stored as plaintext password, or stored as the string "sha1(".sha1_hex($password).")".
MD5 comparison will work with passwords stored as plaintext password, or stored as the string "md5(".md5_hex($password).")".
Plaintext comparison will work with passwords stored as sha1(string), md5(string), plaintext password string, or crypted password.
login_script
Returns a chunk of javascript that will encode the password before the html form is ever submitted. It does require that $ENV{PATH_TRANSLATED} is not modified before calling the require_auth method so that any external javascript files may be served (also by the require_auth).
auth_string_sha1
Arguments are username, password, save_password, and time. This will return a valid login string. You probably will want to pass 1 for the save_password or else the login will only be good for 6 hours.
my $login = $self->auth->auth_string_sha1($user, $pass, 1); my $url = "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?$login";
Using plaintext allows for the password to be passed in the querystring. It should at least be Base64 encoded. I'll add that soon - BUT - really you should be using the SHA1 or MD5 login types.
Paul Seamons <perlspam at seamons dot com>
1 POD Error
The following errors were encountered while parsing the POD:
You forgot a '=back' before '=head1'
To install CGI::Ex, copy and paste the appropriate command in to your terminal.
cpanm
cpanm CGI::Ex
CPAN shell
perl -MCPAN -e shell install CGI::Ex
For more information on module installation, please visit the detailed CPAN module installation guide.