0

To logout a user, logged in using basic Auth, I am trying to follow advice on: link1 link2

I am using express-basic-auth to protect a few routes in my application. An example of a protected route using it is as follows:

const basicAuth = require('express-basic-auth');
const protect = basicAuth({
  users: { 'admin': 'secret' },
  challenge: true
})
app.get('/protected', protect, function(req, res) {
  console.log("authorization header ", req.headers.authorization);
  console.log("user ", req.auth);
  res.send('Success!');
});

With challenge: true in basicAuth we get a popup to enter username and password. If the entered information is correct, we'll get Success! written on the screen.

The problem occurred when I wanted to logout the user. I researched and found that basic authentication is not built around logouts yet there were a few hacky ways of doing it.

To logout, we basically need to login with the wrong credentials. We can't ask a user to do that so I wanted to write a JavaScript snippet that would login with the wrong credentials and link that code to a logout button.

To logout I created a logout button and forced a logout using fetch with GET method and wrong credentials. The code is as follows:

const logoutLink = document.getElementById("logout");
logout.addEventListener("click", handleLogoutClick)
function handleLogoutClick() {
// Wrong credentials
let username = 'admin';
let password = 'passwd';
let url = `http://localhost:3000/protected`; // url cannot have credentials

let headers = new Headers();


headers.append('Content-Type', 'text/json');
headers.set('Authorization', 'Basic ' + btoa(username + ":" + password));

fetch(url, {method:'GET',
        headers: headers})
.then(response => {
  if (!response.ok) throw new Error(response.status);
  return response.json();})
.then(data => {console.log("Data is", data)})
.catch(err => {console.log("Error is", err)})

The above code snippet worked in logging out a user on Chrome but did not work in Firefox.

In my browser, if I want to logout a user and I do: http://user:wrongPassword@localhost:3000/protected, then it logs out the user (works both for Chrome and Firefox)

However, I can't make a fetch request to http://user:wrongPassword@localhost:3000/protected URL so I have to make a request to http://localhost:3000/protected and passed the credentials as a header.

Also to test what might be wrong with the logout request, I tried making a login request using javascript. It seemed to work when I see the console.log statements in the browser as well as those I get on the terminal where the app is running (get a status 200). But when I try and access a protected route, I get the basicAuth pop-up to enter credentials again. (If I login manually in the browser, it doesn't ask for login credentials again)

The code for the login (which seems to work in terms of the 200 status request I get back) but is not actually working is as follows:

function userForm() {
    const loginForm = document.getElementById('login-form');
    loginForm.addEventListener("submit", handleFormSubmit)
    function handleFormSubmit(e) {
      e.preventDefault();
      
      const formData = new FormData(document.querySelector('form'));
      const formObject = {}
      for (let pair of formData.entries()) {
        formObject[pair[0]] = pair[1];
      }
      console.log(formObject);
      const {username, password} = formObject;
      const url = `http://localhost:3000/protected`;
      let headers = new Headers();
      headers.append('Content-Type', 'text/json');
      headers.set('Authorization', 'Basic ' + btoa(username + ":" + password));

      (async () => {
          try {
            const response = await fetch(url, {
            method: 'GET',
            headers: headers
            });

            console.log("response is", response);
            if (response.status === 200) {
              window.location.href = url;
            }
          } catch (error) {
            console.log("An error occured in form submission");
          }
          
        })(url, headers);
    }
  }
  userForm();

We get a response.status of 200 but when the browser gets redirected to the protected route, I get a pop-up to enter credentials. When I make a fetch call to a protected route, using vanilla JS, I can see the authorization headers and user value printed to terminal (from the console.log statements in app.get('/protected', protect, function(req, res) {}

I know that basicAuth is not something to be used in production. But I would like to know why doing a login like this is not working. And also why logout is not working for firefox but working for chrome. Also are there some resources I can consult which might clear basicAuth for me. The package exxpress-basic-auth is not very clear in this regard.

Closing the browser or refreshing the browser does logout the user. But if I could write a script for a logout button, and maybe also for login, it would be much better.

Hasip Timurtas
  • 983
  • 2
  • 11
  • 20
A. J.
  • 61
  • 4

2 Answers2

0

After searching and experimenting I found out how we can use javascript to logout a user logged in with Basic Auth and also why the method I was using above appeared to work but did not actually work.

Javascript code to logout a user that works (checked in chrome and firefox).

const logoutLink = document.getElementById("logout");
logout.addEventListener("click", handleLogoutClick)
function handleLogoutClick() {
  // url for protected route
  const url = 'http://localhost:3000/users'
  // wrong credentials to remove
  // to trigger a login with wrong credentials
  const username = 'fakeUser';
  const password = 'randomPassword';
  var http = new XMLHttpRequest();
  // xhr request with wrong credentials 
  // so as to flush out the saved correct credentials
  http.open("get", url, false, username, password);
  http.send("");
  // status 401 is for accessing unauthorized route
  // will get this only if attempt to flush out correct credentials was successful
  if (http.status === 401) {
    alert("You're logged out now");
    window.location.href = "/";
  } else {
    alert("Logout failed");
  }
}

This above method works because it establishes a browser session.

Javascript logout where we send wrong credentials using headers.set('Authorization', 'Basic ' + btoa(username + ":" + password)); didn't work because that method works for that one particular request only.

That is why using the method with wrong credentials sent in headers was throwing a 401 (unauthorized request) but for that particular request only. Since it was sending wrong user credentials only for that particular request, it failed to flush out the correct user credentials and logout the user.

A. J.
  • 61
  • 4
0

Just wanted to add to A.J's answer since it worked for me. I wanted to prevent the back button from accessing the page so I added these headers in my express backend which prevented a cached version from loading:

res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
res.header('Expires', '-1');
res.header('Pragma', 'no-cache');

This was taken from this answer.

Jelo
  • 1