-
-
Save liujed/5ba4449b22dc481824a8aa13978a9de9 to your computer and use it in GitHub Desktop.
GitHub Auto SSO script for Tampermonkey
This file contains 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
// ==UserScript== | |
// @name Auto-authenticate to organizations on Github | |
// @namespace https://jedliu.net | |
// @version 1.0.1 | |
// @description Periodically authenticate to organizations on Github. | |
// @author Jed Liu | |
// @match https://github.com/* | |
// ==/UserScript== | |
// Adapted from https://gist.github.com/pozil/252d4b04ec7308723a88ed4632b24c2c. | |
( | |
function() { | |
'use strict'; | |
// == BEGIN CONFIGURATION ================================================= | |
// The main page on which this script runs. Must be in the github.com | |
// domain. | |
const GITHUB_TRIGGER_URL = 'https://github.com/pulls'; | |
// The Github organizations requiring SSO. | |
const SSO_ORGS = [ | |
'postman-eng', | |
'postmanlabs', | |
]; | |
// How frequently to do SSO, in milliseconds. | |
const AUTH_INTERVAL_ms = 1000 * 60 * 60 * 23; | |
// == END CONFIGURATION =================================================== | |
const SSO_URL_REGEX = /https:\/\/github\.com\/orgs\/([a-z\-]+)\/sso/; | |
const SAML_INITIATE_URL_REGEX = | |
/https:\/\/github\.com\/orgs\/[a-z\-]+\/saml\/initiate/; | |
const COOKIE_KEY = 'pm-auto-gh-sso'; | |
const url = window.location.href; | |
var context = loadRuntimeContext(); | |
if (context.isActive) { | |
if (url === GITHUB_TRIGGER_URL) { | |
continueSsoFromTriggerPage(); | |
} else if (SSO_URL_REGEX.test(url)) { | |
// SSO start confirmation page. | |
retry(continueFromSsoPage, 10); | |
} else if (SAML_INITIATE_URL_REGEX.test(url)) { | |
// SSO end confirmation page. | |
retry(skipSsoOnSamlError, 10); | |
} | |
} else if (url === GITHUB_TRIGGER_URL) { | |
retry(scheduleNextSsoAttempts, 10); | |
} | |
// Resume/start SSO process from the trigger page. | |
function continueSsoFromTriggerPage() { | |
if (context.isSigningIn) { | |
// SSO just succeeded. | |
context.ssoSuccessOrgs.push(SSO_ORGS[context.orgIdx]); | |
context.isSigningIn = false; | |
context.orgIdx++; | |
saveRuntimeContext(context); | |
} | |
if (SSO_ORGS.length === context.orgIdx) { | |
// Done with all organizations. | |
context.isActive = false; | |
context.orgIdx = 0; | |
context.nextAuthTime = Date.now() + AUTH_INTERVAL_ms; | |
saveRuntimeContext(context); | |
retry(scheduleNextSsoAttempts, 10); | |
return true; | |
} | |
// Sign into the next organization. | |
const org = SSO_ORGS[context.orgIdx]; | |
const returnUrl = encodeURIComponent(GITHUB_TRIGGER_URL); | |
window.location = | |
`https://github.com/orgs/${org}/sso?return_to=${returnUrl}`; | |
return true; | |
} | |
// Automatically clicks on the Continue button in an SSO transition page. | |
function continueFromSsoPage() { | |
const continueButton = document.querySelector('button[type=submit]'); | |
if (continueButton) { | |
context.isSigningIn = true; | |
saveRuntimeContext(context); | |
continueButton.click(); | |
return true; | |
} | |
return false; | |
} | |
// Skips org sign-in if a warning is displayed (e.g., unauthorized IP | |
// address). | |
function skipSsoOnSamlError() { | |
const warning = document.querySelector('#js-pjax-container svg.octicon-alert'); | |
if (warning) { | |
// SSO failed. | |
const org = SSO_ORGS[context.orgIdx]; | |
context.ssoFailedOrgs.push(org); | |
context.isSigningIn = false; | |
saveRuntimeContext(context); | |
window.location = GITHUB_TRIGGER_URL; | |
return true; | |
} | |
return false; | |
} | |
// Schedules the next round of SSO attempts according to | |
// context.nextAuthTime. | |
function scheduleNextSsoAttempts() { | |
var delay = context.nextAuthTime - Date.now(); | |
if (delay < 0) { | |
delay = 0; | |
} | |
window.setTimeout(() => { | |
// Start authenticating. | |
context.isActive = true; | |
saveRuntimeContext(context); | |
retry(continueSsoFromTriggerPage, 10); | |
}, delay); | |
return true; | |
} | |
// Runs a function a certain number of times until it reports success. Each | |
// attempt is separated by a small break. | |
// | |
// @param f {function} the function to be retried. Must return a boolean | |
// that indicates success. | |
// @param retries {number} the number of retries left before we abort. | |
function retry(f, retries) { | |
const success = f(); | |
if (success) return; | |
retries--; | |
if (retries === 0) { | |
log('Giving up after several retries'); | |
return; | |
} | |
setTimeout(() => { | |
retry(f, retries); | |
}, 250); | |
} | |
function saveRuntimeContext() { | |
document.cookie = | |
`${COOKIE_KEY}=${escape(JSON.stringify(context))}; path=/`; | |
} | |
function loadRuntimeContext() { | |
const name = `${COOKIE_KEY}=`; | |
const cookieArray = document.cookie.split(';').filter( | |
function(cookie) { | |
return cookie.trim().indexOf(name) === 0; | |
} | |
); | |
if (cookieArray.length) { | |
return JSON.parse( | |
unescape(cookieArray[0].trim().substring(name.length)) | |
); | |
} | |
return { | |
// Scheduled time for next round of SSO attempts. | |
nextAuthTime: 0, | |
// Whether we are actively authenticating to Github organizations. | |
isActive: false, | |
// If isActive, this points to the entry in SSO_ORGS to which we | |
// actively authenticating. Otherwise, points to the entry in SSO_ORGS | |
// to which we will authenticate next. | |
orgIdx: 0, | |
// Whether we have started authentication against a Github | |
// organization. | |
isSigningIn: false, | |
// List of Github organizations for which SSO succeeded. | |
ssoSuccessOrgs: [], | |
// List of Github organizations for which SSO failed. | |
ssoFailedOrgs: [], | |
}; | |
} | |
function log(message) { | |
console.log(`Github auto SSO: ${message}`); | |
} | |
} | |
)(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment