Skip to content

Instantly share code, notes, and snippets.

@vpArth
Created May 17, 2024 19:25
Show Gist options
  • Save vpArth/b137f010dba72c7b1b20cb8cff68b524 to your computer and use it in GitHub Desktop.
Save vpArth/b137f010dba72c7b1b20cb8cff68b524 to your computer and use it in GitHub Desktop.
Show your TOTPs and update password files
{
"name": "totp",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"speakeasy": "^2.0.0"
}
}
// simple json key-value dictionary, where key is title and value is a totp-secret
// value can be also an array [secret, filename, template] to persist fresh token into file
// template should contain {{token}} placeholder to be replaced
const credentials = require('./secrets.json');
const process = require('node:process');
const finalizers = [];
process.on('SIGINT', () => {
finalizers.forEach(cb => cb());
});
const totp = require('speakeasy');
class Generator {
constructor(title, secret, callback) {
this.title = title;
this.secret = secret;
this.callback = callback;
this.lastToken = null;
}
start() {
this._i = setInterval(() => this.gen(), 1000);
this.gen();
}
stop() {
if (this._i) {
clearInterval(this._i);
this._i = null;
}
}
gen() {
const token = totp.totp({secret: this.secret, encoding: 'base32'});
if (token !== this.lastToken) {
this.callback(token, this.title);
this.lastToken = token;
}
}
}
///////////////////////////////////
const actual = {};
for (let [title, secret] of Object.entries(credentials)) {
let filename = null;
let template = '{{token}}\n'
if (Array.isArray(secret)) {
[secret, filename, template] = secret;
}
const generator = new Generator(title, secret, (token) => {
actual[title] = token;
if (filename) {
require('fs').writeFile(
filename,
template.replace(/{{\s*token\s*}}/g, token),
{flag: 'w'},
err => err && console.error(err));
}
console.clear();
console.table(actual);
});
generator.start();
finalizers.push(() => generator.stop());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment