Last active
May 16, 2025 15:20
-
-
Save angeloevangelista/fc5effcbef0ff83fb02d2ef3a5f1972d to your computer and use it in GitHub Desktop.
OKTA autologin
This file contains hidden or 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
| 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", | |
| }); | |
| }) | |
| ); |
This file contains hidden or 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
| 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