# ----------------------------------------------------------------------------- # web_auth / logoff.pl # note - see http://stackoverflow.com/questions/233507/how-to-log-out-user-from-web-site-using-basic-authentication # for a possible alternative # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # date description # ---------- ----------------------------------------------------------------- # ----------------------------------------------------------------------------- use strict; use warnings; use Apache2::Const -compile => qw(AUTH_REQUIRED); use Apache2::Request; use Apache2::RequestIO; use Apache2::RequestUtil; # ----------------------------------------------------------------------------- # # How the logoff links are implemented # ------------------------------------ # # A typical logoff link in our web pages looks like this: # # Log off # or # Log off # # where: # # id is "logoff" optionally followed by an underscore and one or more digits. # # href should be to "logoff.pl" in a password protected directory. It can be # followed by extra path information and a query string - see details below. # # main.js attaches an event handler to links which have the logoff* id. The event # handler tries to execute the ClearAuthenticationCache command, which will only # succeed in IE. # # If the ClearAuthenticationCache succeeded, the logoff.pl page is loaded. If # it did not succeed, the handler first modifies the url to include a user name # and password of "Unknown:Invalid"; this is done by inserting "Unknown:Invalid@" # between the "//" and the hostname in the full url (see note). For example, # # http://scott-dev.ito.lacoe.edu/data_collection/logoff.pl # becomes # http://Unknown:Invalid@scott-dev.ito.lacoe.edu/data_collection/logoff.pl # # note: even though the href in the example link above is relative, in javascript # the href property of the link returns a full url. # # Because the user:password is invalid, a 401 error is returned and all tested # browsers re-prompt for a username and password. # # There are a couple of issues with this technique. The minor one is that the # browser will prefill the user name in the prompt with "Unknown" (Safari). # # The larger issue, which this program deals with, is that the browser might # keep the "Unknown:Invalid@" as part of the url for the page, so if you log # in from the password prompt, all relative urls on the page will have the # invalid credential included. # # To solve this issue, this program does a redirect to a full url; since the # url doesn't contain the username and password, this becomes the base url # for the browser. # # Generally, either this program would be used dorectly, or there would be a # symlink in the protected application directory e.g., htdocs/workshops/admin/ # to this program in htdocs/web_auth/ # # *** NOTE NOTE NOTE NOTE NOTE *** # # In order to use this program directly, the AuthName for the application you # are logging out from must be the same as the AuthName for /web_auth/, which is # AuthName "ITO" # # Details on the href and the redirect url # ---------------------------------------- # # Ideally, the user would exit the browser after logging out. However, they, # or another user at the same computer, might want to log back in with different # credentials. The appropriate landing page after the new login would probably # be a menu page (perhaps password protected) for the same application. This # can be done in one of two ways, depending on how the application directory is # configured: # # 1. If there is directory index for the protected application directory, # that is what should be displayed, the href would simply be to "login.pl" # in the directory (login.pl would be a symlink as described above) # # e.g., to go to the (public) index for TPB after logging out, the href # would just be "logoff.pl", assuming that the base url for the web page # with the logout link is /TPB/. # # 2. If you want to execute a specific program in the application directory # (for example, if the directory index isn't password protected and you # want instead to invoke the application with the password protected # index) the href would be "logoff.pl" followed by extra path info # (and optionally a query string) to the application. # # e.g., in /data_collection/ or /data_collection/eett/, you could use # "logoff.pl/data_collection.pl?run_mode=menu", or, since the default # run mode for data_collection.pl is "menu", just "logoff.pl/data_collection.pl" # # Note that these two methods require that the desired landing page is a # password protected directory. If it isn't (e.g., to go to /staff after leaving # /workshops/admin), you can instead use a special query string. If the query # string starts with a slash it is treated as the full path to the new landing # page. So, for example, to go to /staff when logging out from /workshops/admin, # the href would be: # # "logoff.pl?/staff" # # I don't know if this would ever be appropriate, but you can even include a # query string. So if you don't want to bother with a symlink, you could do: # # "/web_auth/logoff.pl?/data_collection/eett/data_collection.pl?run_mode=menu # # ----------------------------------------------------------------------------- my $r = Apache2::RequestUtil->request; my $hostname = $r->hostname; my $port = $ENV{'SERVER_PORT'}; my $uri = $r->uri; my $path_info = $r->path_info; my $query_string = $r->args || ''; # determine if this is http or https my $protocol = ($port == 443) ? 'https' : 'http'; # start building the redirect location my $location = "${protocol}://${hostname}"; # add port number if it is non-standard if ($port != 80 && $port != 443) { $location .= ":${port}"; } # now add the new path and query string if ($query_string =~ /^\//) { # if the query string starts with a slash, that is the new path $location .= $query_string; } else { # remove this application from the uri, then add the query string, if any (my $path = $uri) =~ s!/logoff.pl.*$!!; $location .= $path . $path_info; if ($query_string) { $location .= '?' . $query_string; } } #warn(" #login after logoff: # host $hostname # port $port # uri $uri # path info $path_info # query string $query_string # protocol $protocol # location $location #"); $r->headers_out->set(Location => $location); $r->status(Apache2::Const::REDIRECT); return Apache2::Const::REDIRECT; # -----------------------------------------------------------------------------