Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Loging a user out with CGI and Cookies?

by r.joseph (Hermit)
on Apr 15, 2001 at 23:42 UTC ( [id://72691] : perlquestion . print w/replies, xml ) Need Help??

r.joseph has asked for the wisdom of the Perl Monks concerning the following question:

Hello! This is a pretty dumb question this time, but for some reason, I can't seem to get this to work correctly. I did everything by the docs, and yet I still can't seem to get it to work. Here is my problem:

I have a CGI program that uses cookies (hopefully merlyn won't -- me for not reading his latest Web Techniques article well enough *smiles*) to maintain a users 'login'. To create the cookies, this code is exectued:
my $q = new CGI; my $cke1 = new CGI::Cookie( -name => $in{usr}, -value => $val, -expires => '+1h',); print $q->header(-cookie=>$cke1);
which seems to work very well - by the way, the value is the users password MD5ed with some other secret stuff (I could tell you, but then I would have to kill you).

Now, I wanted to write a simple routine to log the person out, so that their little cookie would simply disapear into the magnetic oblivion that is the users hard drive. Easy, right? All I have to do is create another cookie, by the same name, with an expiration that is negative! So here is what I tried:
my $q = new CGI; my $cke1 = new CGI::Cookie( -name => $usr, -value => $pwd, -expires => '-1d',); print $q->redirect( -cookie=>$cke1, -uri=>'');
Now, as you can see, the cookie is the same, I just changed the expiration from an hour ahead of when the cookie was made (as was in the login code) to a day previous of when the code was executed, therefore telling the computer that that patricular cookie should have expired ASAP! Then, a simple redirect header takes the user to the login page, setting them up to log right back in!

Now, am I missing something here, because this seems like a perfectly logical way to do things, but it DOESN'T work! The logout code executes fine, but the cookie doesn't actually expire, so it is futile!

I am tired, and don't feel well, so I may be missing something, so please help me out fellow monks! Thanks!

r. j o s e p h
"Violence is a last resort of the incompetent" - Salvor Hardin, Foundation by Issac Asimov

Replies are listed 'Best First'.
Re: Loging a user out with CGI and Cookies?
by Keef (Sexton) on Apr 16, 2001 at 00:58 UTC
    Well, I'm assuming that your not simply checking for the existence of a cookie with your CGI, but rather, you are looking for some specific verifier tht the cookie holds. Why not just recode your script to return an error page if the cookie holds an invalid verifier?

    To clarify: Use a session-based system. When a user logs in, write them a cookie with the session ID. At that same time, write the session ID to a local file. When the user is logged out, erase the session ID from the file and if they try to return, their cookie will be checked. When the file is checked for the session ID that their cookie holds, the script won't find anything and can then tell the user to login again or buzzoff.
Re: Loging a user out with CGI and Cookies?
by Beatnik (Parson) on Apr 16, 2001 at 01:21 UTC
    I recommend looking into lhoward's node on Cookie logins... I personally found it pretty handy for my last few nodes...

    ... Quidquid perl dictum sit, altum viditur.
Re: Loging a user out with CGI and Cookies?
by merlyn (Sage) on Apr 16, 2001 at 15:52 UTC
    You should definitely read my article on branding a browser with a cookie.

    In there, I say something like: do not use the presence of a cookie to indicate a logged-in state, because the browser is free to ignore your requests to remove the cookie or expire it after a while.

    Instead, simply ignore that particular cookie if it's sent in the future. This means you should use a cookie only as a key to a server-side database which shows the current state of logged-in or not.

    -- Randal L. Schwartz, Perl hacker

      Hi Merlyn,
      maybe I am wrong but I thing that there is litle mistake in your code. Line 34:

      my $cache = File::Cache->;new({namespace => 'cookiemaker',

      Should be without ";":

      my $cache = File::Cache->new({namespace => 'cookiemaker',

      This probabably happened by copying line 33.

      Your servant Li Tin O've Weedle
      mad Tsort's philosopher

        That's an artifact (read: "bug") from pod2html, which is why I also provide the companion colNN.listing.txt file in the same directory.

        -- Randal L. Schwartz, Perl hacker

      Thanks merlyn - shortly after posting this, I broke out the latest "Web Techbniques" and re-read your article a couple times. Your right - as I was testing new ideas, I noticed that sometimes my browser did in fact ignore the cookie expiry all together. Therefore, I am going to need to implement some type of server side session info, has had been previously suggested.

      Thanks for all the help!

      r. j o s e p h
      "Violence is a last resort of the incompetent" - Salvor Hardin, Foundation by Issac Asimov
        Merlyn was right again,
        but should be interesting to know which one browser has problems..... Microsoft Internet Exploder? Netsuck?
        I am using MSIE 5.0 (PC) and I founded some diferencies between PC and Mac version - right with cookie and redirect operations.


        Li Tin O've Weedle
        mad Tsort's philosopher

Re: Loging a user out with CGI and Cookies?
by Dominus (Parson) on Apr 16, 2001 at 08:26 UTC
    Says r.joseph:
    as you can see, the cookie is the same...
    But it isn't. In the first case, you have -name => $in{usr} and in the second case you have -name => $usr.

    Perhaps the problem is that $in{usr} and $usr don't match?

Re: Loging a user out with CGI and Cookies?
by Trimbach (Curate) on Apr 16, 2001 at 02:49 UTC
    I believe your problem is that you're trying to pass a cookie header at the same time as a redirect header. AFAIK that's a no-no: If a browser receives a "redirect" http header that's all that's processed, you don't get to pass any bonus information like cookie headers and such.

    The solution is to recode your CGI to display a "you are logged out" message (which is good form anyway) instead of a redirect. Then you CAN pass the cookie, and all will be right with the world.

    Gary Blackburn
    Trained Killer

      Trimbach, you may be half right. But it is possible to accomplish this without a two-step process.

      First, the browser can indeed handle the cookie header and the "Location: ..." header line in the same server response. The Location line must come last and it must end with two new-lines.

      But I can find no indication in my quick re-visiting of the docs that the CGI redirect function allows the specification of a cookie along with the redirect. (Update: but see the following response by athomason.) And the CGI pod says don't print a header along with a redirect.

      So I accomplish this manually:

      print "Set-Cookie: $usr=x;expires=-1d\n"; print "Location:\n\n";
      There may be a gotcha here but I haven't run into it.

      BTW, are you sure that $in{usr} and $usr have the same value? That would, of course, be crucial to what you are trying to do.

        As dvergin points out, browsers are perfectly capable of handling cookies sent with redirects. Fortunately, also supports it, though you have to dig a bit to be sure. The CGI pod indeed does not mention such a procedure, but it is supported. Browsing the code of the redirect sub in you can see:
        #### Method: redirect # Return a Location: style header # #### 'redirect' => <<'END_OF_FUNC', sub redirect { my($self,@p) = self_or_default(@_); my($url,$target,$cookie,$nph,@other) = $self->rearrange([[LOCATION +,URI,URL],TARGET,COOKIE,NPH],@p); $url = $url || $self->self_url; my(@o); foreach (@other) { tr/\"//d; push(@o,split("=",$_,2)); } unshift(@o, '-Status'=>'302 Moved', '-Location'=>$url, '-nph'=>$nph); unshift(@o,'-Target'=>$target) if $target; unshift(@o,'-Cookie'=>$cookie) if $cookie; unshift(@o,'-Type'=>''); return $self->header(@o); }

        Note in particular the -Cookie bit. And nicely enough, this actually works as intended. I've used bits like the following successfully:

        $cookie = cookie( -name => $COOKIE_NAME, -value => $session_key, -expires => $COOKIE_EXPIRE, -path => $SCRIPT_PATH, -domain => $SCRIPT_DOMAIN, -secure => 0 ); ... print redirect( -uri => 'view.cgi', -cookie => $cookie );

        I admit I'm not sure why OP's bit fails. r.jospeh, take a look at the cookie files for the site you're connecting to in order to make sure they're correct. Offhand, I'd suspect (like dvergin) a difference in $in{usr} and $usr is the problem. If not, you could set up a dirty HTTP server (with HTTP::Daemon, for instance) to see what's going on with your logout cookie.

Re: Loging a user out with CGI and Cookies?
by LiTinOveWeedle (Scribe) on Apr 16, 2001 at 15:17 UTC
    Some times ago I wrote same script and one good brother in perl help me with this. So now try to help you...

    He re is part of my program:

    use CGI; $query = new CGI; sub logout { $cookie_expires = "-1s"; &cookie_create; print $query->redirect( -uri => $redirect_to, -cookie => $cookie ) +; } sub cookie_create { $cookie = $query->cookie(-name => $cookie_name, -expires => $cookie_expires, -domain => $cookie_domain, -path => $cookie_path, -value=>{ username => $username, password => crypt($password, $us +ername) } ); }

    As you can see cookie_create sub can be also used to create cookie not only to erase it. This worked well, if you want all script which verify login again csv flat file user database I can send it to you.

    Good luck

    Li Tin O've Weedle
    mad Tsort's philosopher

Re: Loging a user out with CGI and Cookies?
by dvergin (Monsignor) on Apr 16, 2001 at 06:21 UTC
    r.joseph, while we are scratching around for an answer... one more point that you do not make clear: How do you know that "the cookie doesn't actually expire"?
      Because I tested it locally with my cache folder open - the cookie didn't expire, and I will still able to access all the site's functions. However, many MANY good ideas have been brought up, and I thank everyone for your input. I think I will end up combining many facets of these suggestions to find a solution. Thanks a bunch!

      r. j o s e p h
      "Violence is a last resort of the incompetent" - Salvor Hardin, Foundation by Issac Asimov