Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
valeriangalliat committed Aug 24, 2021
0 parents commit 4dddf34
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/node_modules
/package-lock.json
/public/bulma.min.css
/public/*.js
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TOTP with your password manager that doesn't support TOTP 😅
70 changes: 70 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const QrScanner = require('qr-scanner')
const totp = require('totp-generator')

QrScanner.WORKER_PATH = '/qr-scanner-worker.min.js'

const preview = document.getElementById('preview')
const previewVideo = preview.querySelector('video')
const form = document.getElementById('form')
const result = document.getElementById('result')
const resultCode = document.getElementById('code')
const { password, submit, scan, cancel } = form.elements

function generateCode () {
const code = totp(password.value)
result.classList.remove('is-hidden')
result.style.lineHeight = `${resultCode.offsetHeight}px`
resultCode.size = code.length - 2 // Not sure why but this fits perfectly.
resultCode.value = code
resultCode.select()
navigator.clipboard.writeText(code)
}

form.addEventListener('submit', e => {
e.preventDefault()
generateCode()
})

password.addEventListener('change', () => {
generateCode()
})

const qrScanner = new QrScanner(previewVideo, result => {
scan.classList.remove('is-hidden')
cancel.classList.add('is-hidden')
preview.classList.add('is-hidden')
qrScanner.stop()

const secret = new URLSearchParams(new URL(result).search).get('secret')

password.value = secret

// Don't call `form.submit()` because it wouldn't trigger the event listener.
submit.click()
})

// Force width to avoid pixel shift with rounding.
scan.style.width = `${scan.offsetWidth}px`

scan.addEventListener('click', e => {
e.preventDefault()
cancel.style.width = `${scan.offsetWidth}px`
previewVideo.width = form.offsetWidth
scan.classList.add('is-hidden')
cancel.classList.remove('is-hidden')
result.classList.add('is-hidden')
preview.classList.remove('is-hidden')
qrScanner.start()
})

cancel.addEventListener('click', e => {
e.preventDefault()
scan.classList.remove('is-hidden')
cancel.classList.add('is-hidden')
preview.classList.add('is-hidden')
qrScanner.stop()
})

resultCode.addEventListener('click', e => {
resultCode.select()
})
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"scripts": {
"build": "cp node_modules/bulma/css/bulma.min.css public && cp node_modules/qr-scanner/qr-scanner-worker.min.js public && browserify main.js -o public/main.js",
"watch": "watchify main.js -o public/main.js",
"lint": "standard --fix"
},
"dependencies": {
"browserify": "^17.0.0",
"bulma": "^0.9.3",
"qr-scanner": "^1.2.0",
"totp-generator": "^0.0.9"
},
"devDependencies": {
"standard": "^16.0.3",
"watchify": "^4.0.0"
}
}
62 changes: 62 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TOTP with your password manager that doesn't support TOTP 😅</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/bulma.min.css">
</head>
<body class="section">
<div class="container is-max-desktop has-text-centered">
<h1 class="title is-3 mb-6">TOTP with your password manager that doesn't support TOTP 😅</h1>
<form class="m-auto" id="form" style="display: table">
<div class="field has-addons">
<div class="control">
<button class="button" name="scan" type="button">Scan code 🤳</button>
<button class="button is-danger is-hidden" name="cancel" type="button">Cancel</button>
</div>
<div class="control">
<input class="input" type="password" name="password" placeholder="TOTP secret">
</div>
<div class="control">
<button class="button is-info" name="submit" type="submit">Get TOTP! 🚀</button>
</div>
</div>
</form>
<div class="mt-5 is-hidden" id="result">
Your code is <input class="input is-inline" id="code"></span>. Copied to clipboard! 🎉
</div>
<div class="mt-2 is-hidden" id="preview">
<video class="is-block m-auto"></video>
</div>
<h2 class="title is-4 mt-6">How to use this? 🤔</h2>
<div class="content">
<ul>
<li>Scan TOTP QR code or paste plaintext TOTP secret</li>
<li>Submit so you can save the TOTP secret in your password manager</li>
<li><strong>Tip:</strong> use the username field to give it a meaningful name</li>
<li>When you come back, focus the password field to select amongst all the secrets you saved</li>
<li>Profit 😎</li>
</ul>
</div>
<h2 class="title is-4">Do you steal my secrets? 😱</h2>
<div class="content">
<details>
<summary><strong>No.</strong></summary>
<div class="message is-info mt-4">
<div class="message-body has-text-left">
<p>This works by tricking your password manager into saving your TOTP secrets as if they were user passwords on this domain.</p>
<p>We use the secret that you input by scanning a QR code or using your password manager autofill to generate a one-time password on your request.</p>
<p>The secret never leaves your browser.</p>
</div>
</div>
</details>
</div>
<h2 class="title is-4">View the code on GitHub!</h2>
<div class="content">
<p><strong><a href="https://github.com/valeriangalliat/totp">I love viewing the code! 😍</a></strong></p>
</div>
</div>
<script src="main.js"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"github": {
"silent": true
}
}

0 comments on commit 4dddf34

Please sign in to comment.