Skip to content

Instantly share code, notes, and snippets.

@alexey-max-fedorov
Created July 23, 2026 19:34
Show Gist options
  • Select an option

  • Save alexey-max-fedorov/d7eecee5800db11b6f17c9dead827dfd to your computer and use it in GitHub Desktop.

Select an option

Save alexey-max-fedorov/d7eecee5800db11b6f17c9dead827dfd to your computer and use it in GitHub Desktop.
UH Microsoft 365 sign-in speed-up — auto-advances the MFA proof-up "Next" prompt (cancellable 2s countdown) and auto-skips the Authenticator install page. Defers the optional setup prompts; does NOT disable MFA.

UH MFA Auto-Advance + Skip Setup

A small Tampermonkey / Violentmonkey userscript that speeds up the University of Houston Microsoft 365 sign-in flow.

What it does

Microsoft 365 (UH CougarNet) sign-in nags you to "set up another way to verify" and to install the Microsoft Authenticator app. This script clears those two optional prompts for you:

  1. Proof-up "Next" page (login.microsoftonline.com) — when the "Let's keep your account secure" screen appears, the script starts a 2-second countdown (the button reads Next (2)Next (1) → auto-click). A note is added to the page explaining it.
  2. Authenticator install page (mysignins.microsoft.com) — auto-clicks Skip setup.

Cancelling the countdown

Before the 2 seconds elapse, press any key or click anywhere to cancel. The button resets to Next and nothing is clicked — so you're free to pick "Use a different account" or set up MFA normally.

Important — this does NOT disable MFA

This only defers/skips the optional setup prompts. It does not disable, weaken, or bypass multi-factor authentication on your account. If your org requires MFA, it will still be required — this just stops the repeated "set it up now" nagging on each sign-in.

Install

  1. Install a userscript manager (Tampermonkey or Violentmonkey).
  2. Create a new script and paste in uh-mfa-auto-advance.user.js, or open the raw file to have the manager prompt an install.
  3. Make sure "Allow User Scripts" / developer mode is enabled if your browser requires it.

Notes

  • The selectors are generic Microsoft 365 elements, so it will also work for other Microsoft 365 orgs — only the name is UH-specific.
  • MIT licensed. Author: Alexey Fedorov (@alexey-max-fedorov).
// ==UserScript==
// @name UH MFA Auto-Advance + Skip Setup
// @namespace https://alexey-fedorov.com
// @version 1.0
// @description Speeds up the University of Houston Microsoft 365 sign-in flow: auto-advances the "set up another verification method" proof-up prompt after a cancellable 2s countdown, and auto-skips the Microsoft Authenticator install page. This only DEFERS/skips the optional MFA setup prompts — it does NOT disable, weaken, or bypass multi-factor authentication.
// @author Alexey Fedorov (alexey-max-fedorov)
// @homepageURL https://alexey-fedorov.com
// @supportURL https://github.com/alexey-max-fedorov
// @match https://login.microsoftonline.com/*
// @match https://mysignins.microsoft.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const TAG = '[UH MFA]';
function fireClick(el) {
['mousedown', 'mouseup', 'click'].forEach(type => {
el.dispatchEvent(new MouseEvent(type, {
bubbles: true,
cancelable: true,
view: window,
}));
});
}
// ============================================================
// STEP 2 — mysignins.microsoft.com: click "Skip setup"
// ============================================================
function trySkip() {
// Guard: only proceed if the heading says "Install Microsoft Authenticator"
const heading = [...document.querySelectorAll('h2')].find(
h => h.textContent.trim() === 'Install Microsoft Authenticator'
);
if (!heading) return false;
const skipBtn = [...document.querySelectorAll('button')].find(
b => b.textContent.trim() === 'Skip setup'
);
if (skipBtn) {
console.log(`${TAG} Heading confirmed + skip button found, clicking...`);
fireClick(skipBtn);
return true;
}
return false;
}
// ============================================================
// STEP 1 — login.microsoftonline.com: auto-advance "Next"
// with a cancellable 2s countdown.
// ============================================================
const NEXT_ID = 'idSubmit_ProofUp_Redirect';
const NOTE_ID = 'uh-mfa-autoadvance-note';
let armed = false;
let cancelled = false;
let originalValue = 'Next';
const timers = [];
function upsertNote(text) {
const boiler = document.getElementById('idBoilerPlateText');
if (!boiler) return; // best-effort — countdown still runs without it
let note = document.getElementById(NOTE_ID);
if (!note) {
note = document.createElement('p');
note.id = NOTE_ID;
const b = document.createElement('b');
note.appendChild(b);
boiler.insertBefore(note, boiler.firstChild); // prepend as top child
}
note.firstChild.textContent = text;
}
function onUserInput(e) { cancelCountdown(e.type); }
function addCancelListeners() {
window.addEventListener('keydown', onUserInput, true);
window.addEventListener('mousedown', onUserInput, true);
}
function removeCancelListeners() {
window.removeEventListener('keydown', onUserInput, true);
window.removeEventListener('mousedown', onUserInput, true);
}
function cancelCountdown(reason) {
if (!armed || cancelled) return;
cancelled = true;
timers.forEach(clearTimeout);
removeCancelListeners();
const btn = document.getElementById(NEXT_ID);
if (btn) btn.value = originalValue; // restore "Next"
upsertNote('Auto-continue cancelled — click Next yourself, or choose "Use a different account".');
console.log(`${TAG} countdown cancelled via ${reason}`);
}
function armCountdown() {
if (armed) return true;
const btn = document.getElementById(NEXT_ID);
if (!btn || btn.offsetParent === null) return false; // not present / not visible yet
armed = true;
originalValue = btn.value || 'Next';
upsertNote('Auto-continuing in 2s — press any key or click anywhere to cancel (e.g. to use a different account).');
addCancelListeners();
btn.value = 'Next (2)';
console.log(`${TAG} Next detected — auto-advancing in 2s (2)...`);
timers.push(setTimeout(() => {
if (cancelled) return;
btn.value = 'Next (1)';
console.log(`${TAG} ...(1)`);
}, 1000));
timers.push(setTimeout(() => {
if (cancelled) return;
removeCancelListeners(); // remove BEFORE our synthetic click so it doesn't self-cancel
btn.value = originalValue; // restore label, then click
console.log(`${TAG} countdown elapsed — auto-clicking Next`);
fireClick(btn);
}, 2000));
return true;
}
// ============================================================
// Shared observer — a page is only ever one of the two hosts,
// so only the matching handler will ever fire.
// ============================================================
const observer = new MutationObserver(() => {
if (trySkip() || armCountdown()) observer.disconnect();
});
observer.observe(document.body, { childList: true, subtree: true });
// Initial attempt in case the target is already in the DOM at run time.
if (trySkip() || armCountdown()) observer.disconnect();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment