For the dashboard looks like it's a http-authentication protocol. Unfortunately this does not persist through closing the browser. The dashboard does allow custom middleware to handle the authentication instead of the default one. If you remove httpNodeAuth & httpStaticAuth and handle the users within the middleware, it should get to what you want:
ui: {
path: "ui",
middleware: require('./dashboardMiddleware')
},
dashboardMiddleware.js @ root (or you can put this all in the settings file)
module.exports = (req,res,next) => {
// To get a hashed password run the following command:
// node -e "console.log(require('bcryptjs').hashSync(process.argv[1], 8));" your-password-here
// sessionExpiryTime is duration in seconds for session to remain valid
const settings = {
userAuth: {
type: "credentials",
users: [
{
username: "admin",
password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
permissions: "*"
},{
username: "user",
password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
permissions: "*"
}
]
},
sessionFile: './.sessions.json',
sessionExpiryTime: 604800
}
const sessionInfo = {
headers_accept_language: req.headers['accept-language'],
headers_host: req.headers.host,
headers_user_agent: req.headers['user-agent'],
headers_client_address: req.header('x-forwarded-for') || req.connection.remoteAddress
};
const bcrypt = require('bcryptjs');
const createSession = (scope) => {
const sessionHash = bcrypt.hashSync(JSON.stringify(sessionInfo), 8);
const expires = Date.now() + (settings.sessionExpiryTime*1000);
const client = 'node-red-dashboard';
return {sessionHash, expires, client, scope}
}
const fs = require('fs');
let session = {};
try {
if (fs.existsSync(settings.sessionFile)) {
session = require(settings.sessionFile);
}
} catch (err) {
console.log({err});
}
const tokens = [];
Object.keys(session).forEach(sessionKey => tokens.push(sessionKey));
for (let i = 0; i < tokens.length; i++) {
if (bcrypt.compareSync(JSON.stringify(sessionInfo), tokens[i])) {
if (session[tokens[i]].expires < Date.now()) {
delete session[tokens[i]];
fs.writeFileSync(settings.sessionFile, JSON.stringify(session));
res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
return res.sendStatus(401);
} else {
return next();
}
}
}
const authHeader = req.headers.authorization;
if (authHeader) {
const token = authHeader.split(' ')[1];
const [username, pass] = new Buffer.from(token, 'base64').toString('ascii').split(':');
for (let i = 0; i < settings.userAuth.users.length; i++) {
if (username === settings.userAuth.users[i].username) {
if(bcrypt.compareSync(pass, settings.userAuth.users[i].password)) {
const sessionJSON = createSession(settings.userAuth.users[i].permissions);
session[sessionJSON.sessionHash] = sessionJSON;
fs.writeFileSync(settings.sessionFile, JSON.stringify(session));
return next();
}
}
};
res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
return res.sendStatus(401);
}
res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
return res.sendStatus(401);
};
This creates a fingerprint
of your device and allows repeated logins for a duration of time that you set. IP address changes and browser changes/updates will require logging in again. This does not overwrite any existing sessions so the same account login works from multiple devices.