I'm trying to create a simple login form using Node/Express/Passport/Javascript with Basic authentication, but can't get it to work. What I want is to have a page where you input your credentials and hit "login". If these credentials are correct, then the user should be redirected to a page that is protected by basic auth, without asking for my credentials again. This seems like it should be really simple to do, but I keep running into problems.
Currently, I have a page where you input your login credentials (username and password). Hitting login makes an axios POST request to a /login endpoint where it verifies the login credentials. This endpoint returns status code 200 if the credentials are correct. If the response is 200 Success, I redirect the user to the basic-auth protected page. The server is using passport express middleware to serialises the user and maintain a user session. But this information seems to be lost on redirect and I can't find a way to set the basic auth headers on the redirect. Hence, the protected page I redirected to asks me for my credentials again. I've tried to fix it by sending the basic auth credentials again:
window.location.replace(http://username:password@website/directory/blahblah)
but most modern browsers seem to strip out basic auth credentials, so that doesn't work.
I was able to get it to work by putting the username and password in the url:
window.location.replace(http://website/directory/blahblah?username=uname&password=password)
but that seems like a terrible and insecure way of solving what should have a simple solution, since this method means that the username and password will end up appearing in my browser history. Is there a better way to implement such a login form?
The server code is a standard express server that uses passport.authenticate middleware with sessions enabled. What I find weird is that when my POST request returns, the req.session.passport.user contains the serialised user, so the code seems to be working properly, so I suspect that there is an issue with my HTML code. My current HTML login form is as follows:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<form method="get" action="javascript:loginFn()">
<input name="username" type="text" value="uname" >
<input name="password" type="password" value="pword">
<input type="submit">
</form>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
const endpoint = 'login';
const nextEndpoint = 'authorize';
//function redirect(url, method) {
// var form = document.createElement('form');
// form.method = method;
// form.action = url;
// document.body.appendChild(form);
// form.submit();
//};
async function loginFn(){
const username = document.querySelector('form [name=username]');
const password = document.querySelector('form [name=password]');
const uriParts = location.href.split('://');
const http = uriParts[0];
uriParts.splice(0, 1);
var targetUri = `${http}://${username.value}:${password.value}@${uriParts.join('://').replace(endpoint, nextEndpoint)}`;
// If credentials valid, the response code will be 200 Success
const res = await axios
.request({
baseURL: targetUri,
// Username and password used for basic auth
method: 'POST',
auth: {
username: username.value,
password: password.value,
},
params: {
login_only: true,
},
})
.catch((err) => {
// Unable to connect to the server, redirect to the NoConnection page.
if (err.message === 'Network Error') {
throw new Error('Unstable database connection');
}
if(err.request.status === 404){
throw new Error('Invalid callback URL');
}
if (err.request.status === 401) {
// Incorrect login credentials
throw new Error('Invalid login credentials');
}
});
if(res.status === 200){
// How do I NOT put the username and password in the URL redirect
window.location.replace(targetUri + '?username=uname&password=pword');
}
}
</script>
</body>
</html>