The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

CGI::Ex::Auth - Handle logins nicely.

SYNOPSIS

  ### 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;
  }

DESCRIPTION

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.

METHODS

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".

The name of the auth cookie. Default is "ce_auth".

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";

TODO

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.

AUTHORS

Paul Seamons <perlspam at seamons dot com>

1 POD Error

The following errors were encountered while parsing the POD:

Around line 821:

You forgot a '=back' before '=head1'