-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 4dddf34
Showing
6 changed files
with
159 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# TOTP with your password manager that doesn't support TOTP 😅 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"github": { | ||
"silent": true | ||
} | ||
} |