'How to hide password in the nodejs console?
I want to hide password input. I see many answers in stackoverflow but I can't verify value if I press backspace. The condition return false.
I tried several solution to overwrite the function but I got an issue with buffer if I press backspace, I got invisible character \b.
I press : "A", backspace, "B", I have in my buffer this : "\u0041\u0008\u0042" (toString() = 'A\bB') and not "B".
I have :
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("password : ", function(password) {
console.log("Your password : " + password);
});
Solution 1:[1]
Overwrite _writeToOutput of application's readline interface : https://github.com/nodejs/node/blob/v9.5.0/lib/readline.js#L291
To hide your password input, you can use :
FIRST SOLUTION : "password : [=-]"
This solution has animation when you press a touch :
password : [-=]
password : [=-]
The code :
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.stdoutMuted = true;
rl.query = "Password : ";
rl.question(rl.query, function(password) {
console.log('\nPassword is ' + password);
rl.close();
});
rl._writeToOutput = function _writeToOutput(stringToWrite) {
if (rl.stdoutMuted)
rl.output.write("\x1B[2K\x1B[200D"+rl.query+"["+((rl.line.length%2==1)?"=-":"-=")+"]");
else
rl.output.write(stringToWrite);
};
This sequence "\x1B[2K\x1BD" uses two escapes sequences :
- Esc [2K : clear entire line.
- Esc D : move/scroll window up one line.
To learn more, read this : http://ascii-table.com/ansi-escape-sequences-vt-100.php
SECOND SOLUTION : "password : ****"
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.stdoutMuted = true;
rl.question('Password: ', function(password) {
console.log('\nPassword is ' + password);
rl.close();
});
rl._writeToOutput = function _writeToOutput(stringToWrite) {
if (rl.stdoutMuted)
rl.output.write("*");
else
rl.output.write(stringToWrite);
};
You can clear history with :
rl.history = rl.history.slice(1);
Solution 2:[2]
This can be handled with readline by intercepting the output through a muted stream, as is done in the read project on npm (https://github.com/isaacs/read/blob/master/lib/read.js):
var readline = require('readline');
var Writable = require('stream').Writable;
var mutableStdout = new Writable({
write: function(chunk, encoding, callback) {
if (!this.muted)
process.stdout.write(chunk, encoding);
callback();
}
});
mutableStdout.muted = false;
var rl = readline.createInterface({
input: process.stdin,
output: mutableStdout,
terminal: true
});
rl.question('Password: ', function(password) {
console.log('\nPassword is ' + password);
rl.close();
});
mutableStdout.muted = true;
Solution 3:[3]
You can use the readline-sync module instead of node's readline.
Password-hiding functionality is built in via it's "hideEchoBack" option.
Solution 4:[4]
Another method using readline:
var readline = require("readline"),
rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.input.on("keypress", function (c, k) {
// get the number of characters entered so far:
var len = rl.line.length;
// move cursor back to the beginning of the input:
readline.moveCursor(rl.output, -len, 0);
// clear everything to the right of the cursor:
readline.clearLine(rl.output, 1);
// replace the original input with asterisks:
for (var i = 0; i < len; i++) {
rl.output.write("*");
}
});
rl.question("Enter your password: ", function (pw) {
// pw == the user's input:
console.log(pw);
rl.close();
});
Solution 5:[5]
My solution, scraped together from various bits online:
import readline from 'readline';
export const hiddenQuestion = query => new Promise((resolve, reject) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const stdin = process.openStdin();
process.stdin.on('data', char => {
char = char + '';
switch (char) {
case '\n':
case '\r':
case '\u0004':
stdin.pause();
break;
default:
process.stdout.clearLine();
readline.cursorTo(process.stdout, 0);
process.stdout.write(query + Array(rl.line.length + 1).join('*'));
break;
}
});
rl.question(query, value => {
rl.history = rl.history.slice(1);
resolve(value);
});
});
Usage is like this:
// import { hiddenQuestion } from './hidden-question.js';
const main = async () => {
console.log('Enter your password and I will tell you your password! ');
const password = await hiddenQuestion('> ');
console.log('Your password is "' + password + '". ');
};
main().catch(error => console.error(error));
Solution 6:[6]
Wanted to add to the marked solution#2.
When we detect the line-ends, I believe we should remove the event handler instead of just stdin.pause(). This can be an issue if you are waiting on rl.question/rl.prompt elsewhere.
In those cases, if stdin.pause() was used, it would just exit the program without giving any errors and can be quite annoying to debug.
function hidden(query, callback) {
var stdin = process.openStdin();
var onDataHandler = function(char) {
char = char + "";
switch (char) {
case "\n": case "\r": case "\u0004":
// Remove this handler
stdin.removeListener("data",onDataHandler);
break;//stdin.pause(); break;
default:
process.stdout.write("\033[2K\033[200D" + query + Array(rl.line.length+1).join("*"));
break;
}
}
process.stdin.on("data", onDataHandler);
rl.question(query, function(value) {
rl.history = rl.history.slice(1);
callback(value);
});
}
Solution 7:[7]
Also one can use tty.ReadStream
changing mode of process.stdin
to disable echoing input characters.
let read_Line_Str = "";
let credentials_Obj = {};
process.stdin.setEncoding('utf8');
process.stdin.setRawMode( true );
process.stdout.write( "Enter password:" );
process.stdin.on( 'readable', () => {
const chunk = process.stdin.read();
if ( chunk !== null ) {
read_Line_Str += chunk;
if(
chunk == "\n" ||
chunk == "\r" ||
chunk == "\u0004"
){
process.stdout.write( "\n" );
process.stdin.setRawMode( false );
process.stdin.emit('end'); /// <- this invokes on.end
}else{
// providing visual feedback
process.stdout.write( "*" );
}
}else{
//console.log( "readable data chunk is null|empty" );
}
} );
process.stdin.on( 'end', () => {
credentials_Obj.user = process.env.USER;
credentials_Obj.host = 'localhost';
credentials_Obj.database = process.env.USER;
credentials_Obj.password = read_Line_Str.trim();
credentials_Obj.port = 5432;
//
connect_To_DB( credentials_Obj );
} );
Solution 8:[8]
You can use the prompt module, as suggested here.
const prompt = require('prompt');
const properties = [
{
name: 'username',
validator: /^[a-zA-Z\s\-]+$/,
warning: 'Username must be only letters, spaces, or dashes'
},
{
name: 'password',
hidden: true
}
];
prompt.start();
prompt.get(properties, function (err, result) {
if (err) { return onErr(err); }
console.log('Command-line input received:');
console.log(' Username: ' + result.username);
console.log(' Password: ' + result.password);
});
function onErr(err) {
console.log(err);
return 1;
}
Solution 9:[9]
A promisified typescript native version:
This will also handle multiple question calls (as @jeffrey-woo pointed out). I chose not to replace input with *, as it didn't feel very unix-y, and I found it to be glitchy sometimes if typing too fast anway.
import readline from 'readline';
export const question = (question: string, options: { hidden?: boolean } = {}) =>
new Promise<string>((resolve, reject) => {
const input = process.stdin;
const output = process.stdout;
type Rl = readline.Interface & { history: string[] };
const rl = readline.createInterface({ input, output }) as Rl;
if (options.hidden) {
const onDataHandler = (charBuff: Buffer) => {
const char = charBuff + '';
switch (char) {
case '\n':
case '\r':
case '\u0004':
input.removeListener('data', onDataHandler);
break;
default:
output.clearLine(0);
readline.cursorTo(output, 0);
output.write(question);
break;
}
};
input.on('data', onDataHandler);
}
rl.question(question, (answer) => {
if (options.hidden) rl.history = rl.history.slice(1);
rl.close();
resolve(answer);
});
});
Usage:
(async () => {
const hiddenValue = await question('This will be hidden', { hidden: true });
const visibleValue = await question('This will be visible');
console.log('hidden value', hiddenValue);
console.log('visible value', visibleValue);
});
Solution 10:[10]
Here's my solution which doesn't require any external libraries (besides readline) or a lot of code.
// turns off echo, but also doesn't process backspaces
// also captures ctrl+c, ctrl+d
process.stdin.setRawMode(true);
const rl = require('readline').createInterface({input: process.stdin});
rl.on('close', function() { process.exit(0); }); // on ctrl+c, doesn't work? :(
rl.on('line', function(line) {
if (/\u0003\.test(line)/) process.exit(0); // on ctrl+c, but after return :(
// process backspaces
while (/\u007f/.test(line)) {
line = line.replace(/[^\u007f]\u007f/, '').replace(/^\u007f+/, '');
}
// do whatever with line
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
