[http] How to log out user from web site using BASIC authentication?

Is it possible to log out user from a web site if he is using basic authentication?

Killing session is not enough, since, once user is authenticated, each request contains login info, so user is automatically logged in next time he/she access the site using the same credentials.

The only solution so far is to close browser, but that's not acceptable from the usability standpoint.

This question is related to http authentication basic-authentication

The answer is


Have the user click on a link to https://log:[email protected]/. That will overwrite existing credentials with invalid ones; logging them out.


The following function is confirmed working for Firefox 40, Chrome 44, Opera 31 and IE 11.
Bowser is used for browser detection, jQuery is also used.

- secUrl is the url to a password protected area from which to log out.
- redirUrl is the url to a non password protected area (logout success page).
- you might wish to increase the redirect timer (currently 200ms).

_x000D_
_x000D_
function logout(secUrl, redirUrl) {_x000D_
    if (bowser.msie) {_x000D_
        document.execCommand('ClearAuthenticationCache', 'false');_x000D_
    } else if (bowser.gecko) {_x000D_
        $.ajax({_x000D_
            async: false,_x000D_
            url: secUrl,_x000D_
            type: 'GET',_x000D_
            username: 'logout'_x000D_
        });_x000D_
    } else if (bowser.webkit) {_x000D_
        var xmlhttp = new XMLHttpRequest();_x000D_
        xmlhttp.open("GET", secUrl, true);_x000D_
        xmlhttp.setRequestHeader("Authorization", "Basic logout");_x000D_
        xmlhttp.send();_x000D_
    } else {_x000D_
        alert("Logging out automatically is unsupported for " + bowser.name_x000D_
            + "\nYou must close the browser to log out.");_x000D_
    }_x000D_
    setTimeout(function () {_x000D_
        window.location.href = redirUrl;_x000D_
    }, 200);_x000D_
}
_x000D_
_x000D_
_x000D_


  • use a session ID (cookie)
  • invalidate the session ID on the server
  • Don't accept users with invalid session IDs

You can do it entirely in JavaScript:

IE has (for a long time) standard API for clearing Basic Authentication cache:

document.execCommand("ClearAuthenticationCache")

Should return true when it works. Returns either false, undefined or blows up on other browsers.

New browsers (as of Dec 2012: Chrome, FireFox, Safari) have "magic" behavior. If they see a successful basic auth request with any bogus other username (let's say logout) they clear the credentials cache and possibly set it for that new bogus user name, which you need to make sure is not a valid user name for viewing content.

Basic example of that is:

var p = window.location.protocol + '//'
// current location must return 200 OK for this GET
window.location = window.location.href.replace(p, p + 'logout:password@')

An "asynchronous" way of doing the above is to do an AJAX call utilizing the logout username. Example:

(function(safeLocation){
    var outcome, u, m = "You should be logged out now.";
    // IE has a simple solution for it - API:
    try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
    // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
    if (!outcome) {
        // Let's create an xmlhttp object
        outcome = (function(x){
            if (x) {
                // the reason we use "random" value for password is 
                // that browsers cache requests. changing
                // password effectively behaves like cache-busing.
                x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                x.send("")
                // x.abort()
                return 1 // this is **speculative** "We are done." 
            } else {
                return
            }
        })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u ))
    }
    if (!outcome) {
        m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
    }
    alert(m)
    // return !!outcome
})(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)

You can make it a bookmarklet too:

javascript:(function(c){var a,b="You should be logged out now.";try{a=document.execCommand("ClearAuthenticationCache")}catch(d){}a||((a=window.XMLHttpRequest?new window.XMLHttpRequest:window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):void 0)?(a.open("HEAD",c||location.href,!0,"logout",(new Date).getTime().toString()),a.send(""),a=1):a=void 0);a||(b="Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser.");alert(b)})(/*pass safeLocation here if you need*/);


This JavaScript must be working for all latest version browsers:

//Detect Browser
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
    // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
    // At least Safari 3+: "[object HTMLElementConstructor]"
var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var Host = window.location.host;


//Clear Basic Realm Authentication
if(isIE){
//IE
    document.execCommand("ClearAuthenticationCache");
    window.location = '/';
}
else if(isSafari)
{//Safari. but this works mostly on all browser except chrome
    (function(safeLocation){
        var outcome, u, m = "You should be logged out now.";
        // IE has a simple solution for it - API:
        try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
        // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
        if (!outcome) {
            // Let's create an xmlhttp object
            outcome = (function(x){
                if (x) {
                    // the reason we use "random" value for password is 
                    // that browsers cache requests. changing
                    // password effectively behaves like cache-busing.
                    x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                    x.send("");
                    // x.abort()
                    return 1 // this is **speculative** "We are done." 
                } else {
                    return
                }
            })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u )) 
        }
        if (!outcome) {
            m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
        }
        alert(m);
        window.location = '/';
        // return !!outcome
    })(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)
}
else{
//Firefox,Chrome
    window.location = 'http://log:out@'+Host+'/';
}

This isn't directly possible with Basic-Authentication.

There's no mechanism in the HTTP specification for the server to tell the browser to stop sending the credentials that the user already presented.

There are "hacks" (see other answers) typically involving using XMLHttpRequest to send an HTTP request with incorrect credentials to overwrite the ones originally supplied.


An addition to the answer by bobince ...

With Ajax you can have your 'Logout' link/button wired to a Javascript function. Have this function send the XMLHttpRequest with a bad username and password. This should get back a 401. Then set document.location back to the pre-login page. This way, the user will never see the extra login dialog during logout, nor have to remember to put in bad credentials.


type chrome://restart in the address bar and chrome, with all its apps that are running in background, will restart and the Auth password cache will be cleaned.


The following function is confirmed working for Firefox 40, Chrome 44, Opera 31 and IE 11.
Bowser is used for browser detection, jQuery is also used.

- secUrl is the url to a password protected area from which to log out.
- redirUrl is the url to a non password protected area (logout success page).
- you might wish to increase the redirect timer (currently 200ms).

_x000D_
_x000D_
function logout(secUrl, redirUrl) {_x000D_
    if (bowser.msie) {_x000D_
        document.execCommand('ClearAuthenticationCache', 'false');_x000D_
    } else if (bowser.gecko) {_x000D_
        $.ajax({_x000D_
            async: false,_x000D_
            url: secUrl,_x000D_
            type: 'GET',_x000D_
            username: 'logout'_x000D_
        });_x000D_
    } else if (bowser.webkit) {_x000D_
        var xmlhttp = new XMLHttpRequest();_x000D_
        xmlhttp.open("GET", secUrl, true);_x000D_
        xmlhttp.setRequestHeader("Authorization", "Basic logout");_x000D_
        xmlhttp.send();_x000D_
    } else {_x000D_
        alert("Logging out automatically is unsupported for " + bowser.name_x000D_
            + "\nYou must close the browser to log out.");_x000D_
    }_x000D_
    setTimeout(function () {_x000D_
        window.location.href = redirUrl;_x000D_
    }, 200);_x000D_
}
_x000D_
_x000D_
_x000D_


This isn't directly possible with Basic-Authentication.

There's no mechanism in the HTTP specification for the server to tell the browser to stop sending the credentials that the user already presented.

There are "hacks" (see other answers) typically involving using XMLHttpRequest to send an HTTP request with incorrect credentials to overwrite the ones originally supplied.


I've just tested the following in Chrome (79), Firefox (71) and Edge (44) and it works fine. It applies the script solution as others noted above.

Just add a "Logout" link and when clicked return the following html

    <div>You have been logged out. Redirecting to home...</div>    

<script>
    var XHR = new XMLHttpRequest();
    XHR.open("GET", "/Home/MyProtectedPage", true, "no user", "no password");
    XHR.send();

    setTimeout(function () {
        window.location.href = "/";
    }, 3000);
</script>

An addition to the answer by bobince ...

With Ajax you can have your 'Logout' link/button wired to a Javascript function. Have this function send the XMLHttpRequest with a bad username and password. This should get back a 401. Then set document.location back to the pre-login page. This way, the user will never see the extra login dialog during logout, nor have to remember to put in bad credentials.


  • use a session ID (cookie)
  • invalidate the session ID on the server
  • Don't accept users with invalid session IDs

function logout() {
  var userAgent = navigator.userAgent.toLowerCase();

  if (userAgent.indexOf("msie") != -1) {
    document.execCommand("ClearAuthenticationCache", false);
  }

  xhr_objectCarte = null;

  if(window.XMLHttpRequest)
    xhr_object = new XMLHttpRequest();
  else if(window.ActiveXObject)
    xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
  else
    alert ("Your browser doesn't support XMLHTTPREQUEST");

  xhr_object.open ('GET', 'http://yourserver.com/rep/index.php', false, 'username', 'password');
  xhr_object.send ("");
  xhr_object = null;

  document.location = 'http://yourserver.com'; 
  return false;
}

I updated mthoring's solution for modern Chrome versions:

function logout(secUrl, redirUrl) {
    if (bowser.msie) {
        document.execCommand('ClearAuthenticationCache', 'false');
    } else if (bowser.gecko) {
        $.ajax({
            async: false,
            url: secUrl,
            type: 'GET',
            username: 'logout'
        });
    } else if (bowser.webkit || bowser.chrome) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open(\"GET\", secUrl, true);
        xmlhttp.setRequestHeader(\"Authorization\", \"Basic logout\");\
        xmlhttp.send();
    } else {
// http://stackoverflow.com/questions/5957822/how-to-clear-basic-authentication-details-in-chrome
        redirUrl = url.replace('http://', 'http://' + new Date().getTime() + '@');
    }
    setTimeout(function () {
        window.location.href = redirUrl;
    }, 200);
}

Sending https://invalid_login@hostname works fine everywhere except Safari on Mac (well, not checked Edge but should work there too).

Logout doesn't work in Safari when a user selects 'remember password' in the HTTP Basic Authentication popup. In this case the password is stored in Keychain Access (Finder > Applications > Utilities > Keychain Access (or CMD+SPACE and type "Keychain Access")). Sending https://invalid_login@hostname doesn't affect Keychain Access, so with this checkbox it is not possible to logout on Safari on Mac. At least it is how it works for me.

MacOS Mojave (10.14.6), Safari 12.1.2.

The code below works fine for me in Firefox (73), Chrome (80) and Safari (12). When a user navigates to a logout page the code is executed and drops the credentials.

    //It should return 401, necessary for Safari only
    const logoutUrl = 'https://example.com/logout'; 
    const xmlHttp = new XMLHttpRequest();
    xmlHttp.open('POST', logoutUrl, true, 'logout');
    xmlHttp.send();

Also for some reason Safari doesn't save credentials in the HTTP Basic Authentication popup even when the 'remember password' is selected. The other browsers do this correctly.


This isn't directly possible with Basic-Authentication.

There's no mechanism in the HTTP specification for the server to tell the browser to stop sending the credentials that the user already presented.

There are "hacks" (see other answers) typically involving using XMLHttpRequest to send an HTTP request with incorrect credentials to overwrite the ones originally supplied.


  • use a session ID (cookie)
  • invalidate the session ID on the server
  • Don't accept users with invalid session IDs

I've just tested the following in Chrome (79), Firefox (71) and Edge (44) and it works fine. It applies the script solution as others noted above.

Just add a "Logout" link and when clicked return the following html

    <div>You have been logged out. Redirecting to home...</div>    

<script>
    var XHR = new XMLHttpRequest();
    XHR.open("GET", "/Home/MyProtectedPage", true, "no user", "no password");
    XHR.send();

    setTimeout(function () {
        window.location.href = "/";
    }, 3000);
</script>

An addition to the answer by bobince ...

With Ajax you can have your 'Logout' link/button wired to a Javascript function. Have this function send the XMLHttpRequest with a bad username and password. This should get back a 401. Then set document.location back to the pre-login page. This way, the user will never see the extra login dialog during logout, nor have to remember to put in bad credentials.


This JavaScript must be working for all latest version browsers:

//Detect Browser
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
    // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
    // At least Safari 3+: "[object HTMLElementConstructor]"
var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var Host = window.location.host;


//Clear Basic Realm Authentication
if(isIE){
//IE
    document.execCommand("ClearAuthenticationCache");
    window.location = '/';
}
else if(isSafari)
{//Safari. but this works mostly on all browser except chrome
    (function(safeLocation){
        var outcome, u, m = "You should be logged out now.";
        // IE has a simple solution for it - API:
        try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
        // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
        if (!outcome) {
            // Let's create an xmlhttp object
            outcome = (function(x){
                if (x) {
                    // the reason we use "random" value for password is 
                    // that browsers cache requests. changing
                    // password effectively behaves like cache-busing.
                    x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                    x.send("");
                    // x.abort()
                    return 1 // this is **speculative** "We are done." 
                } else {
                    return
                }
            })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u )) 
        }
        if (!outcome) {
            m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
        }
        alert(m);
        window.location = '/';
        // return !!outcome
    })(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)
}
else{
//Firefox,Chrome
    window.location = 'http://log:out@'+Host+'/';
}

It's actually pretty simple.

Just visit the following in your browser and use wrong credentials: http://username:[email protected]

That should "log you out".


All you need is redirect user on some logout URL and return 401 Unauthorized error on it. On error page (which must be accessible without basic auth) you need to provide a full link to your home page (including scheme and hostname). User will click this link and browser will ask for credentials again.

Example for Nginx:

location /logout {
    return 401;
}

error_page 401 /errors/401.html;

location /errors {
    auth_basic off;
    ssi        on;
    ssi_types  text/html;
    alias /home/user/errors;
}

Error page /home/user/errors/401.html:

<!DOCTYPE html>
<p>You're not authorised. <a href="<!--# echo var="scheme" -->://<!--# echo var="host" -->/">Login</a>.</p>

 function logout(url){
    var str = url.replace("http://", "http://" + new Date().getTime() + "@");
    var xmlhttp;
    if (window.XMLHttpRequest) xmlhttp=new XMLHttpRequest();
    else xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4) location.reload();
    }
    xmlhttp.open("GET",str,true);
    xmlhttp.setRequestHeader("Authorization","Basic xxxxxxxxxx")
    xmlhttp.send();
    return false;
}

  • use a session ID (cookie)
  • invalidate the session ID on the server
  • Don't accept users with invalid session IDs

This is working for IE/Netscape/Chrome :

      function ClearAuthentication(LogOffPage) 
  {
     var IsInternetExplorer = false;    

     try
     {
         var agt=navigator.userAgent.toLowerCase();
         if (agt.indexOf("msie") != -1) { IsInternetExplorer = true; }
     }
     catch(e)
     {
         IsInternetExplorer = false;    
     };

     if (IsInternetExplorer) 
     {
        // Logoff Internet Explorer
        document.execCommand("ClearAuthenticationCache");
        window.location = LogOffPage;
     }
     else 
     {
        // Logoff every other browsers
    $.ajax({
         username: 'unknown',
         password: 'WrongPassword',
             url: './cgi-bin/PrimoCgi',
         type: 'GET',
         beforeSend: function(xhr)
                 {
            xhr.setRequestHeader("Authorization", "Basic AAAAAAAAAAAAAAAAAAA=");
         },

                 error: function(err)
                 {
                    window.location = LogOffPage;
             }
    });
     }
  }


  $(document).ready(function () 
  {
      $('#Btn1').click(function () 
      {
         // Call Clear Authentication 
         ClearAuthentication("force_logout.html"); 
      });
  });          

Based on what I read above I got a simple solution that works on any browser:

1) on you logout page you call an ajax to your login back end. Your login back end must accept logout user. Once the back end accept, the browser clear the current user and assumes the "logout" user.

$.ajax({
    async: false,
    url: 'http://your_login_backend',
    type: 'GET',
    username: 'logout'
});      

setTimeout(function () {
    window.location.href = 'http://normal_index';
}, 200);

2) Now when the user got back to the normal index file it will try to automatic enter in the system with the user "logout", on this second time you must block it by reply with 401 to invoke the login/password dialog.

3) There are many ways to do that, I created two login back ends, one that accepts the logout user and one that doesn't. My normal login page use the one that doesn't accept, my logout page use the one that accepts it.


Just for the record, there is a new HTTP Response Header called Clear-Site-Data. If your server reply includes a Clear-Site-Data: "cookies" header, then the authentication credentials (not only cookies) should be removed. I tested it on Chrome 77 but this warning shows on the console:

Clear-Site-Data header on 'https://localhost:9443/clear': Cleared data types:
"cookies". Clearing channel IDs and HTTP authentication cache is currently not
supported, as it breaks active network connections.

And the auth credentials aren't removed, so this doesn't works (for now) to implement basic auth logouts, but maybe in the future will. Didn't test on other browsers.

References:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data

https://www.w3.org/TR/clear-site-data/

https://github.com/w3c/webappsec-clear-site-data

https://caniuse.com/#feat=mdn-http_headers_clear-site-data_cookies


type chrome://restart in the address bar and chrome, with all its apps that are running in background, will restart and the Auth password cache will be cleaned.


Here's a very simple Javascript example using jQuery:

function logout(to_url) {
    var out = window.location.href.replace(/:\/\//, '://log:out@');

    jQuery.get(out).error(function() {
        window.location = to_url;
    });
}

This log user out without showing him the browser log-in box again, then redirect him to a logged out page


This is working for IE/Netscape/Chrome :

      function ClearAuthentication(LogOffPage) 
  {
     var IsInternetExplorer = false;    

     try
     {
         var agt=navigator.userAgent.toLowerCase();
         if (agt.indexOf("msie") != -1) { IsInternetExplorer = true; }
     }
     catch(e)
     {
         IsInternetExplorer = false;    
     };

     if (IsInternetExplorer) 
     {
        // Logoff Internet Explorer
        document.execCommand("ClearAuthenticationCache");
        window.location = LogOffPage;
     }
     else 
     {
        // Logoff every other browsers
    $.ajax({
         username: 'unknown',
         password: 'WrongPassword',
             url: './cgi-bin/PrimoCgi',
         type: 'GET',
         beforeSend: function(xhr)
                 {
            xhr.setRequestHeader("Authorization", "Basic AAAAAAAAAAAAAAAAAAA=");
         },

                 error: function(err)
                 {
                    window.location = LogOffPage;
             }
    });
     }
  }


  $(document).ready(function () 
  {
      $('#Btn1').click(function () 
      {
         // Call Clear Authentication 
         ClearAuthentication("force_logout.html"); 
      });
  });          

_x000D_
_x000D_
    function logout(secUrl, redirUrl) {_x000D_
        if (bowser.msie) {_x000D_
            document.execCommand('ClearAuthenticationCache', 'false');_x000D_
        } else if (bowser.gecko) {_x000D_
            $.ajax({_x000D_
                async: false,_x000D_
                url: secUrl,_x000D_
                type: 'GET',_x000D_
                username: 'logout'_x000D_
            });_x000D_
        } else if (bowser.webkit) {_x000D_
            var xmlhttp = new XMLHttpRequest();_x000D_
            xmlhttp.open("GET", secUrl, true);_x000D_
            xmlhttp.setRequestHeader("Authorization", "Basic logout");_x000D_
            xmlhttp.send();_x000D_
        } else {_x000D_
            alert("Logging out automatically is unsupported for " + bowser.name_x000D_
                + "\nYou must close the browser to log out.");_x000D_
        }_x000D_
        setTimeout(function () {_x000D_
            window.location.href = redirUrl;_x000D_
        }, 200);_x000D_
    }
_x000D_
_x000D_
_x000D_

I tried using the above in the following way.

?php
    ob_start();
    session_start();
    require_once 'dbconnect.php';

    // if session is not set this will redirect to login page
    if( !isset($_SESSION['user']) ) {
        header("Location: index.php");
        exit;
    }
    // select loggedin users detail
    $res=mysql_query("SELECT * FROM users WHERE userId=".$_SESSION['user']);
    $userRow=mysql_fetch_array($res);
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Welcome - <?php echo $userRow['userEmail']; ?></title>
<link rel="stylesheet" href="assets/css/bootstrap.min.css" type="text/css"  />
<link rel="stylesheet" href="style.css" type="text/css" />

    <script src="assets/js/bowser.min.js"></script>
<script>
//function logout(secUrl, redirUrl)
//bowser = require('bowser');
function logout(secUrl, redirUrl) {
alert(redirUrl);
    if (bowser.msie) {
        document.execCommand('ClearAuthenticationCache', 'false');
    } else if (bowser.gecko) {
        $.ajax({
            async: false,
            url: secUrl,
            type: 'GET',
            username: 'logout'
        });
    } else if (bowser.webkit) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open("GET", secUrl, true);
        xmlhttp.setRequestHeader("Authorization", "Basic logout");
        xmlhttp.send();
    } else {
        alert("Logging out automatically is unsupported for " + bowser.name
            + "\nYou must close the browser to log out.");
    }
    window.location.assign(redirUrl);
    /*setTimeout(function () {
        window.location.href = redirUrl;
    }, 200);*/
}


function f1()
    {
       alert("f1 called");
       //form validation that recalls the page showing with supplied inputs.    
    }
</script>
</head>
<body>

    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="http://www.codingcage.com">Coding Cage</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="http://www.codingcage.com/2015/01/user-registration-and-login-script-using-php-mysql.html">Back to Article</a></li>
            <li><a href="http://www.codingcage.com/search/label/jQuery">jQuery</a></li>
            <li><a href="http://www.codingcage.com/search/label/PHP">PHP</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">

            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
              <span class="glyphicon glyphicon-user"></span>&nbsp;Hi' <?php echo $userRow['userEmail']; ?>&nbsp;<span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="logout.php?logout"><span class="glyphicon glyphicon-log-out"></span>&nbsp;Sign Out</a></li>
              </ul>
            </li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav> 

    <div id="wrapper">

    <div class="container">

        <div class="page-header">
        <h3>Coding Cage - Programming Blog</h3>
        </div>

        <div class="row">
        <div class="col-lg-12" id="div_logout">
        <h1 onclick="logout(window.location.href, 'www.espncricinfo.com')">MichaelA1S1! Click here to see log out functionality upon click inside div</h1>
        </div>
        </div>

    </div>

    </div>

    <script src="assets/jquery-1.11.3-jquery.min.js"></script>
    <script src="assets/js/bootstrap.min.js"></script>


</body>
</html>
<?php ob_end_flush(); ?>

But it only redirects you to new location. No logout.


Just for the record, there is a new HTTP Response Header called Clear-Site-Data. If your server reply includes a Clear-Site-Data: "cookies" header, then the authentication credentials (not only cookies) should be removed. I tested it on Chrome 77 but this warning shows on the console:

Clear-Site-Data header on 'https://localhost:9443/clear': Cleared data types:
"cookies". Clearing channel IDs and HTTP authentication cache is currently not
supported, as it breaks active network connections.

And the auth credentials aren't removed, so this doesn't works (for now) to implement basic auth logouts, but maybe in the future will. Didn't test on other browsers.

References:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data

https://www.w3.org/TR/clear-site-data/

https://github.com/w3c/webappsec-clear-site-data

https://caniuse.com/#feat=mdn-http_headers_clear-site-data_cookies


add this to your application :

@app.route('/logout')
def logout():
    return ('Logout', 401, {'WWW-Authenticate': 'Basic realm="Login required"'})

Here's a very simple Javascript example using jQuery:

function logout(to_url) {
    var out = window.location.href.replace(/:\/\//, '://log:out@');

    jQuery.get(out).error(function() {
        window.location = to_url;
    });
}

This log user out without showing him the browser log-in box again, then redirect him to a logged out page


It's actually pretty simple.

Just visit the following in your browser and use wrong credentials: http://username:[email protected]

That should "log you out".


add this to your application :

@app.route('/logout')
def logout():
    return ('Logout', 401, {'WWW-Authenticate': 'Basic realm="Login required"'})

Based on what I read above I got a simple solution that works on any browser:

1) on you logout page you call an ajax to your login back end. Your login back end must accept logout user. Once the back end accept, the browser clear the current user and assumes the "logout" user.

$.ajax({
    async: false,
    url: 'http://your_login_backend',
    type: 'GET',
    username: 'logout'
});      

setTimeout(function () {
    window.location.href = 'http://normal_index';
}, 200);

2) Now when the user got back to the normal index file it will try to automatic enter in the system with the user "logout", on this second time you must block it by reply with 401 to invoke the login/password dialog.

3) There are many ways to do that, I created two login back ends, one that accepts the logout user and one that doesn't. My normal login page use the one that doesn't accept, my logout page use the one that accepts it.


function logout() {
  var userAgent = navigator.userAgent.toLowerCase();

  if (userAgent.indexOf("msie") != -1) {
    document.execCommand("ClearAuthenticationCache", false);
  }

  xhr_objectCarte = null;

  if(window.XMLHttpRequest)
    xhr_object = new XMLHttpRequest();
  else if(window.ActiveXObject)
    xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
  else
    alert ("Your browser doesn't support XMLHTTPREQUEST");

  xhr_object.open ('GET', 'http://yourserver.com/rep/index.php', false, 'username', 'password');
  xhr_object.send ("");
  xhr_object = null;

  document.location = 'http://yourserver.com'; 
  return false;
}

Sending https://invalid_login@hostname works fine everywhere except Safari on Mac (well, not checked Edge but should work there too).

Logout doesn't work in Safari when a user selects 'remember password' in the HTTP Basic Authentication popup. In this case the password is stored in Keychain Access (Finder > Applications > Utilities > Keychain Access (or CMD+SPACE and type "Keychain Access")). Sending https://invalid_login@hostname doesn't affect Keychain Access, so with this checkbox it is not possible to logout on Safari on Mac. At least it is how it works for me.

MacOS Mojave (10.14.6), Safari 12.1.2.

The code below works fine for me in Firefox (73), Chrome (80) and Safari (12). When a user navigates to a logout page the code is executed and drops the credentials.

    //It should return 401, necessary for Safari only
    const logoutUrl = 'https://example.com/logout'; 
    const xmlHttp = new XMLHttpRequest();
    xmlHttp.open('POST', logoutUrl, true, 'logout');
    xmlHttp.send();

Also for some reason Safari doesn't save credentials in the HTTP Basic Authentication popup even when the 'remember password' is selected. The other browsers do this correctly.


 function logout(url){
    var str = url.replace("http://", "http://" + new Date().getTime() + "@");
    var xmlhttp;
    if (window.XMLHttpRequest) xmlhttp=new XMLHttpRequest();
    else xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4) location.reload();
    }
    xmlhttp.open("GET",str,true);
    xmlhttp.setRequestHeader("Authorization","Basic xxxxxxxxxx")
    xmlhttp.send();
    return false;
}

I updated mthoring's solution for modern Chrome versions:

function logout(secUrl, redirUrl) {
    if (bowser.msie) {
        document.execCommand('ClearAuthenticationCache', 'false');
    } else if (bowser.gecko) {
        $.ajax({
            async: false,
            url: secUrl,
            type: 'GET',
            username: 'logout'
        });
    } else if (bowser.webkit || bowser.chrome) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open(\"GET\", secUrl, true);
        xmlhttp.setRequestHeader(\"Authorization\", \"Basic logout\");\
        xmlhttp.send();
    } else {
// http://stackoverflow.com/questions/5957822/how-to-clear-basic-authentication-details-in-chrome
        redirUrl = url.replace('http://', 'http://' + new Date().getTime() + '@');
    }
    setTimeout(function () {
        window.location.href = redirUrl;
    }, 200);
}

You can do it entirely in JavaScript:

IE has (for a long time) standard API for clearing Basic Authentication cache:

document.execCommand("ClearAuthenticationCache")

Should return true when it works. Returns either false, undefined or blows up on other browsers.

New browsers (as of Dec 2012: Chrome, FireFox, Safari) have "magic" behavior. If they see a successful basic auth request with any bogus other username (let's say logout) they clear the credentials cache and possibly set it for that new bogus user name, which you need to make sure is not a valid user name for viewing content.

Basic example of that is:

var p = window.location.protocol + '//'
// current location must return 200 OK for this GET
window.location = window.location.href.replace(p, p + 'logout:password@')

An "asynchronous" way of doing the above is to do an AJAX call utilizing the logout username. Example:

(function(safeLocation){
    var outcome, u, m = "You should be logged out now.";
    // IE has a simple solution for it - API:
    try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
    // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
    if (!outcome) {
        // Let's create an xmlhttp object
        outcome = (function(x){
            if (x) {
                // the reason we use "random" value for password is 
                // that browsers cache requests. changing
                // password effectively behaves like cache-busing.
                x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                x.send("")
                // x.abort()
                return 1 // this is **speculative** "We are done." 
            } else {
                return
            }
        })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u ))
    }
    if (!outcome) {
        m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
    }
    alert(m)
    // return !!outcome
})(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)

You can make it a bookmarklet too:

javascript:(function(c){var a,b="You should be logged out now.";try{a=document.execCommand("ClearAuthenticationCache")}catch(d){}a||((a=window.XMLHttpRequest?new window.XMLHttpRequest:window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):void 0)?(a.open("HEAD",c||location.href,!0,"logout",(new Date).getTime().toString()),a.send(""),a=1):a=void 0);a||(b="Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser.");alert(b)})(/*pass safeLocation here if you need*/);


Examples related to http

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Axios Delete request with body and headers? Read response headers from API response - Angular 5 + TypeScript Android 8: Cleartext HTTP traffic not permitted Angular 4 HttpClient Query Parameters Load json from local file with http.get() in angular 2 Angular 2: How to access an HTTP response body? What is HTTP "Host" header? Golang read request body Angular 2 - Checking for server errors from subscribe

Examples related to authentication

Set cookies for cross origin requests How Spring Security Filter Chain works What are the main differences between JWT and OAuth authentication? http post - how to send Authorization header? ASP.NET Core Web API Authentication Token based authentication in Web API without any user interface Custom Authentication in ASP.Net-Core Basic Authentication Using JavaScript Adding ASP.NET MVC5 Identity Authentication to an existing project LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db1

Examples related to basic-authentication

Use Invoke-WebRequest with a username and password for basic authentication on the GitHub API How to define the basic HTTP authentication using cURL correctly? Spring Security exclude url patterns in security annotation configurartion Basic HTTP and Bearer Token Authentication HTTP Error 401.2 - Unauthorized You are not authorized to view this page due to invalid authentication headers Calling a rest api with username and password - how to What is the "realm" in basic authentication How to prevent browser to invoke basic auth popup and handle 401 error using Jquery? Proxy Basic Authentication in C#: HTTP 407 error What is the difference between Digest and Basic Authentication?