HTTP authentication logout via PHP

Viewed 119325

What is the correct way to log out of HTTP authentication protected folder?

There are workarounds that can achieve this, but they are potentially dangerous because they can be buggy or don't work in certain situations / browsers. That is why I am looking for correct and clean solution.

18 Answers

Mu. No correct way exists, not even one that's consistent across browsers.

This is a problem that comes from the HTTP specification (section 15.6):

Existing HTTP clients and user agents typically retain authentication information indefinitely. HTTP/1.1. does not provide a method for a server to direct clients to discard these cached credentials.

On the other hand, section 10.4.2 says:

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information.

In other words, you may be able to show the login box again (as @Karsten says), but the browser doesn't have to honor your request - so don't depend on this (mis)feature too much.

Method that works nicely in Safari. Also works in Firefox and Opera, but with a warning.

Location: http://logout@yourserver.example.com/

This tells browser to open URL with new username, overriding previous one.

Workaround (not a clean, nice (or even working! see comments) solution):

Disable his credentials one time.

You can move your HTTP authentication logic to PHP by sending the appropriate headers (if not logged in):

Header('WWW-Authenticate: Basic realm="protected area"');
Header('HTTP/1.0 401 Unauthorized');

And parsing the input with:

$_SERVER['PHP_AUTH_USER'] // httpauth-user
$_SERVER['PHP_AUTH_PW']   // httpauth-password

So disabling his credentials one time should be trivial.

My solution to the problem is the following. You can find the function http_digest_parse , $realm and $users in the second example of this page: http://php.net/manual/en/features.http-auth.php.

session_start();

function LogOut() {
  session_destroy();
  session_unset($_SESSION['session_id']);
  session_unset($_SESSION['logged']);

  header("Location: /", TRUE, 301);   
}

function Login(){

  global $realm;

  if (empty($_SESSION['session_id'])) {
    session_regenerate_id();
    $_SESSION['session_id'] = session_id();
  }

  if (!IsAuthenticated()) {  
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="'.$realm.
   '",qop="auth",nonce="'.$_SESSION['session_id'].'",opaque="'.md5($realm).'"');
    $_SESSION['logged'] = False;
    die('Access denied.');
  }
  $_SESSION['logged'] = True;  
}

function IsAuthenticated(){
  global $realm;
  global $users;


  if  (empty($_SERVER['PHP_AUTH_DIGEST']))
      return False;

  // check PHP_AUTH_DIGEST
  if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
     !isset($users[$data['username']]))
     return False;// invalid username


  $A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
  $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);

  // Give session id instead of data['nonce']
  $valid_response =   md5($A1.':'.$_SESSION['session_id'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

  if ($data['response'] != $valid_response)
    return False;

  return True;
}

Typically, once a browser has asked the user for credentials and supplied them to a particular web site, it will continue to do so without further prompting. Unlike the various ways you can clear cookies on the client side, I don't know of a similar way to ask the browser to forget its supplied authentication credentials.

AFAIK, there's no clean way to implement a "logout" function when using htaccess (i.e. HTTP-based) authentication.

This is because such authentication uses the HTTP error code '401' to tell the browser that credentials are required, at which point the browser prompts the user for the details. From then on, until the browser is closed, it will always send the credentials without further prompting.

The best solution I found so far is (it is sort of pseudo-code, the $isLoggedIn is pseudo variable for http auth):

At the time of "logout" just store some info to the session saying that user is actually logged out.

function logout()
{
  //$isLoggedIn = false; //This does not work (point of this question)
  $_SESSION['logout'] = true;
}

In the place where I check for authentication I expand the condition:

function isLoggedIn()
{
  return $isLoggedIn && !$_SESSION['logout'];
}

Session is somewhat linked to the state of http authentication so user stays logged out as long as he keeps the browser open and as long as http authentication persists in the browser.

There's a lot of great - complex - answers here. In my particular case i found a clean and simple fix for the logout. I have yet to test in Edge. On my page that I have logged in to, I have placed a logout link similar to this:

<a href="https://MyDomainHere.net/logout.html">logout</a>

And in the head of that logout.html page (which is also protected by the .htaccess) I have a page refresh similar to this:

<meta http-equiv="Refresh" content="0; url=https://logout:logout@MyDomainHere.net/" />

Where you would leave the words "logout" in place to clear the username and password cached for the site.

I will admit that if multiple pages needed to be able to be directly logged in to from the beginning, each of those points of entry would need their own corresponding logout.html page. Otherwise you could centralize the logout by introducing an additional gatekeeper step into the process before the actual login prompt, requiring entry of a phrase to reach a destination of login.

I have summarised my solution in an article (https://www.hattonwebsolutions.co.uk/articles/how_to_logout_of_http_sessions) however I have used an ajax call and 2x htaccess files (as suggested in this question: How to logout of an HTTP authentication (htaccess) that works in Google Chrome?).

In short - you:

  1. Create a sub folder with an htaccess file on the same AuthName but require a different user
  2. Send an ajax request to the page (with the wrong username) (which fails) and then trigger a timeout redirect to the logged out page.

This avoids having a secondary popup in the logout folder requesting another username (which would confuse users). My article uses Jquery but it should be possible to avoid this.

Related