Skip to content

Instantly share code, notes, and snippets.

@affix
Last active October 3, 2025 09:17
Show Gist options
  • Select an option

  • Save affix/0f37f44a82488ec4137de9a9a62c14c7 to your computer and use it in GitHub Desktop.

Select an option

Save affix/0f37f44a82488ec4137de9a9a62c14c7 to your computer and use it in GitHub Desktop.
A greesemonkey/tampermonkey/*monkey script to automatically login to github orgs using SSO
// ==UserScript==
// @name Github Auto SSO
// @namespace Violentmonkey Scripts
// @match https://github.com/*
// @grant none
// @version 2.0.0
// @author Keiran Smith <opensource@keiran.scot>
// @description Automatically login to mutiple orgs in Github
// ==/UserScript==
(function () {
'use strict';
const selectors = [
'[partial-name*="global-sso-banner"] button', // fallback, just in case
];
function findTarget() {
for (const sel of selectors) {
const el = document.querySelector(sel);
if (el) return el;
}
return null;
}
function clickLikeAHuman(el) {
const rect = el.getBoundingClientRect();
const x = rect.left + Math.min(10, rect.width / 2);
const y = rect.top + Math.min(10, rect.height / 2);
const base = { bubbles: true, cancelable: true, composed: true, view: window, clientX: x, clientY: y };
el.scrollIntoView({ block: 'center', inline: 'center' });
if (typeof el.focus === 'function') el.focus({ preventScroll: true });
try { el.dispatchEvent(new PointerEvent('pointerdown', { ...base, pointerId: 1, isPrimary: true })); } catch {}
el.dispatchEvent(new MouseEvent('mousedown', base));
try { el.dispatchEvent(new PointerEvent('pointerup', { ...base, pointerId: 1, isPrimary: true })); } catch {}
el.dispatchEvent(new MouseEvent('mouseup', base));
el.dispatchEvent(new MouseEvent('click', base));
}
let done = false;
function tryClick() {
if (done) return;
const el = findTarget();
if (el && !el.disabled) {
console.log('[AutoSSO] clicking:', el.tagName, el.textContent.trim());
clickLikeAHuman(el);
done = true;
disconnectAll();
}
}
const observers = [];
function observe(doc) {
const obs = new MutationObserver(tryClick);
obs.observe(doc, { childList: true, subtree: true });
observers.push(obs);
}
function disconnectAll() { observers.forEach(o => o.disconnect()); }
tryClick();
observe(document);
window.addEventListener('pjax:end', () => { done = false; tryClick(); });
window.addEventListener('turbo:load', () => { done = false; tryClick(); });
window.addEventListener('turbo:render', () => { done = false; tryClick(); });
const start = Date.now();
const interval = setInterval(() => {
if (done || Date.now() - start > 15000) { clearInterval(interval); return; }
tryClick();
}, 400);
})();
(function () {
'use strict';
console.log("[AutoSSO] Finding Links");
const selector = 'a[role="menuitem"][href^="/orgs/"][href*="/sso"]';
let done = false;
function clickAll() {
const links = document.querySelectorAll(selector);
links.forEach(link => {
if (!link.dataset._autoClicked) {
console.log('[AutoSSO] clicking org link:', link.href);
link.dataset._autoClicked = "true";
link.click();
return;
}
});
}
const observer = new MutationObserver(clickAll);
observer.observe(document.body, { childList: true, subtree: true });
clickAll();
window.addEventListener('pjax:end', clickAll);
window.addEventListener('turbo:load', clickAll);
window.addEventListener('turbo:render', clickAll);
const start = Date.now();
const interval = setInterval(() => {
if (done || Date.now() - start > 15000) { clearInterval(interval); return; }
clickAll();
}, 400);
})();
var isSSOPage = window.location.href.indexOf("sso") > 0
if(isSSOPage) {
document.querySelector(".btn-primary").click();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment