Skip to content

Instantly share code, notes, and snippets.

@lezsakdomi
Last active July 29, 2023 16:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lezsakdomi/d453f881a06ab668dd94d6ac65637776 to your computer and use it in GitHub Desktop.
Save lezsakdomi/d453f881a06ab668dd94d6ac65637776 to your computer and use it in GitHub Desktop.
ELTE Neptun TOTP helper
// ==UserScript==
// @name ELTE Neptun TOTP
// @namespace Violentmonkey Scripts
// @match https://neptun.elte.hu/Account/Login2FA
// @grant none
// @version 1.3
// @license MIT
// @author Domonkos Lezsák
// @description Handles ELTE Neptun TOTP 2FA authentication. The user just has to click a button and not bother with an authenticator app, as long as uses the same browser. Setup instructions: Just go to "New TOTP pairing".
// @updateURL https://gist.github.com/lezsakdomi/d453f881a06ab668dd94d6ac65637776/raw/a0f837b9651619254910bb2774d996aeca37eb71/elte.neptun.totp.user.js
// @downloadURL https://gist.github.com/lezsakdomi/d453f881a06ab668dd94d6ac65637776/raw/a0f837b9651619254910bb2774d996aeca37eb71/elte.neptun.totp.user.js
// @supportURL https://gist.github.com/lezsakdomi/d453f881a06ab668dd94d6ac65637776
// ==/UserScript==
async function generateTotp() {
if (!window.getToken) {
await import('https://unpkg.com/jssha@3.3.0/dist/sha.js');
const _requireBackup = window.require;
const _moduleBackup = window.module;
window.require = (pkg) => {
switch(pkg) {
case 'jssha': return window.jsSHA;
default: throw new Error("No module called " + pkg);
}
}
const module = window.module = {exports: {}}
await import('https://unpkg.com/totp-generator@0.0.14/index.js');
window.require = _requireBackup;
window.module = _moduleBackup;
window.getToken = module.exports;
}
return window.getToken(localStorage.getItem('totp:secret'));
}
(async () => {
switch (location.pathname) {
case '/Account/Login2FA': {
const phase = document.querySelector('input[name="Phase"]').value;
document.querySelector('main > h2').insertAdjacentElement('afterend', document.createElement('pre')).innerText = phase;
switch (phase) {
case 'TOTPPreparation': {
document.querySelector('main form > div.form-group > button[type="submit"]').parentElement.insertAdjacentElement('beforebegin', document.createElement('p')).innerHTML = "Note: You have the TOTP userscript installed, it will substitue Microsoft Authenticatior for you. <b>Just click <i>Next</i></b>"
} break
case 'TOTPPairing': {
const key = document.querySelector('main form > div.form-group > button[type="submit"]').parentElement.previousElementSibling.innerText.match(/[A-Z0-9]+\s*$/)[0];
if (confirm("Found TOTP pairing key " + key + ".\nDo you want to save the key to this browser?")) {
localStorage.setItem('totp:secret', key);
document.querySelector('main form').action += "?autofill_totp=1"
document.querySelector('main form > div.form-group > button[type="submit"]').click()
}
} break
case 'TOTPPairingCheck': {
if ((new URLSearchParams(location.search)).get('autofill_totp')) {
document.querySelector('main form input#TOTPCode').value = await generateTotp()
document.querySelector('main form > div.form-group > button[type="submit"]').click()
}
} break
case 'RequestTOTP': {
const $submitButton = document.querySelector('main form > div.form-group > button.btn-primary[type="submit"]');
async function autofillAndLogin() {
document.querySelector('main form input#TOTPCode').value = await generateTotp()
$submitButton.click()
}
if ((new URLSearchParams(location.search)).get('autofill_totp')) {
await autofillAndLogin();
} else if (localStorage.getItem('totp:secret')) {
{
const $btn = $submitButton.insertAdjacentElement('afterend', document.createElement('button'))
$btn.classList.add('btn')
$btn.classList.add('btn-secondary')
$btn.style.marginLeft = '1em'
$btn.addEventListener('click', (evt) => {
localStorage.removeItem('totp:secret')
location.reload();
})
$btn.innerText = "Clear TOTP secret from this browser"
}
{
const $btn = $submitButton.insertAdjacentElement('afterend', document.createElement('button'))
$btn.classList.add('btn')
$btn.classList.add('btn-primary')
$btn.style.marginLeft = '1em'
$btn.addEventListener('click', (evt) => {
evt.preventDefault();
autofillAndLogin();
})
$btn.innerText = "Autofill and log in"
}
{
$submitButton.classList.add('btn-secondary')
$submitButton.classList.remove('btn-primary')
}
}
}
}
} break
case '/Account/Login': {
}
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment