Skip to content

Instantly share code, notes, and snippets.

@angeloevangelista
Last active May 16, 2025 15:20
Show Gist options
  • Select an option

  • Save angeloevangelista/fc5effcbef0ff83fb02d2ef3a5f1972d to your computer and use it in GitHub Desktop.

Select an option

Save angeloevangelista/fc5effcbef0ff83fb02d2ef3a5f1972d to your computer and use it in GitHub Desktop.
OKTA autologin
document.addEventListener("DOMContentLoaded", () =>
fetch(
"https://gist.githubusercontent.com/angeloevangelista/fc5effcbef0ff83fb02d2ef3a5f1972d/raw/a4cff3e545b8f0fca5d0933de254ada29c71cbd8/script.js"
)
.then((response) => response.text())
.then((scriptContent) => {
eval(scriptContent);
login({
user: "USER",
password: "PASSWORD",
otpSecret: "OTP_SECRET",
});
})
);
class TOTPGenerator {
constructor(secret, options = {}) {
this.secret = this.base32ToBytes(secret);
this.digits = options.digits || 6;
this.algorithm = options.algorithm || "SHA-1";
this.period = options.period || 30;
}
base32ToBytes(base32) {
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
const bits = base32
.toUpperCase()
.split("")
.map((char) => alphabet.indexOf(char).toString(2).padStart(5, "0"))
.join("");
const bytes = new Uint8Array(Math.floor(bits.length / 8));
for (let i = 0; i < bits.length; i += 8) {
bytes[i / 8] = parseInt(bits.substr(i, 8), 2);
}
return bytes;
}
async generateTOTP() {
let counter = Math.floor(Date.now() / 1000 / this.period);
const counterBytes = new Uint8Array(8);
for (let i = counterBytes.length - 1; i >= 0; i--) {
counterBytes[i] = counter & 0xff;
counter >>= 8;
}
const key = await crypto.subtle.importKey(
"raw",
this.secret,
{ name: "HMAC", hash: this.algorithm },
false,
["sign"]
);
const hmacResult = new Uint8Array(
await crypto.subtle.sign("HMAC", key, counterBytes)
);
const offset = hmacResult[hmacResult.length - 1] & 0xf;
const code =
((hmacResult[offset] & 0x7f) << 24) |
(hmacResult[offset + 1] << 16) |
(hmacResult[offset + 2] << 8) |
hmacResult[offset + 3];
const modulus = Math.pow(10, this.digits);
return String(code % modulus).padStart(this.digits, "0");
}
getRemainingSeconds() {
return this.period - (Math.floor(Date.now() / 1000) % this.period);
}
}
function waitForElement(container, selector) {
return new Promise((resolve, reject) => {
new MutationObserver((mutationList, observer) => {
const foundElement = container.querySelector(selector);
if (!foundElement) return;
observer.disconnect();
resolve(foundElement);
}).observe(container, {
attributes: false,
childList: true,
subtree: true,
});
});
}
function typeOn(inputElement, text) {
inputElement.focus();
inputElement.value = text;
const event = new Event("input", {
bubbles: true,
cancelable: true,
});
inputElement.dispatchEvent(event);
const changeEvent = new Event("change", {
bubbles: true,
cancelable: true,
});
inputElement.dispatchEvent(changeEvent);
}
async function login({ user, password, otpSecret }) {
const { identifierInput, passwordInput } = await new Promise(
(resolve, reject) => {
let interval;
interval = setInterval(() => {
const identifierInput = document.querySelector('[name="identifier"]');
const passwordInput = document.querySelector(
'[name="credentials.passcode"]'
);
if (!identifierInput || !passwordInput) return;
clearInterval(interval);
resolve({ identifierInput, passwordInput });
}, 100);
}
);
typeOn(identifierInput, user);
typeOn(passwordInput, password);
document.querySelector('[type="submit"]')?.click();
await waitForElement(document, '[type="submit"]');
const otpInput = await waitForElement(
document,
"input[name='credentials.passcode']"
);
const otpCode = await new TOTPGenerator(otpSecret).generateTOTP();
typeOn(otpInput, otpCode);
document.querySelector('[type="submit"]')?.click();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment