Last active
May 12, 2026 11:36
-
-
Save Luckz/6bb085775941ebaca363c722cb8445e3 to your computer and use it in GitHub Desktop.
KinguinAntiAutoLogoutGuard.user.js
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
| // ==UserScript== | |
| // @name [AI] Kinguin anti-auto-logout guard | |
| // @namespace luckz | |
| // @author luckz | |
| // @LLMs GPT-5.3 Codex | |
| // @version 0001 | |
| // @description Block automatic logout on Kinguin | |
| // @match https://www.kinguin.net/* | |
| // @grant none | |
| // @run-at document-start | |
| // @downloadURL https://gist.github.com/Luckz/6bb085775941ebaca363c722cb8445e3/raw/KinguinAntiAutoLogoutGuard.user.js | |
| // @updateURL https://gist.github.com/Luckz/6bb085775941ebaca363c722cb8445e3/raw/KinguinAntiAutoLogoutGuard.user.js | |
| // ==/UserScript== | |
| // ==Thoughts & Docs== | |
| // The Point™: | |
| // the main app bundle behavior is: one tab gets auth failure - broadcasts LOGOUT_EVENT - tabs dispatch logout - all tabs navigate to /oauth/logout. | |
| // broadcast trigger itself is tied to 401 logic not a direct offline event | |
| // losing internet can lead to broken auth/refresh flows, and then next auth calls can return 401, which starts the chain | |
| // Solution attempt: | |
| // 1. Block cross-tab logout broadcast BroadcastChannel for LOGOUT + LOGOUT_EVENT | |
| // 2. Block/guard navigation to /oauth/logout unless it’s a deliberate user logout click | |
| // ==/Thoughts & Docs== | |
| 'use strict'; | |
| const LOGOUT_PATH = '/oauth/logout'; | |
| const LOGOUT_CHANNEL = 'LOGOUT'; | |
| const LOGOUT_EVENT = 'LOGOUT_EVENT'; | |
| // Allow only intentional user logout clicks for a short time window. | |
| let allowManualLogoutUntil = 0; | |
| document.addEventListener('click', (ev) => { | |
| const a = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null; | |
| if (!a) return; | |
| const href = a.getAttribute('href') || ''; | |
| if (href.includes('/logout') || href.includes('/oauth/logout')) { | |
| allowManualLogoutUntil = Date.now() + 10000; | |
| } | |
| }, true); | |
| function shouldBlockLogoutUrl(urlLike) { | |
| try { | |
| const u = new URL(String(urlLike), location.href); | |
| if (!u.pathname.startsWith(LOGOUT_PATH)) return false; | |
| return Date.now() >= allowManualLogoutUntil; | |
| } catch { | |
| return false; | |
| } | |
| } | |
| // 1) Block cross-tab forced logout broadcast. | |
| if (window.BroadcastChannel) { | |
| const NativeBC = window.BroadcastChannel; | |
| window.BroadcastChannel = function (name) { | |
| const ch = new NativeBC(name); | |
| if (name === LOGOUT_CHANNEL) { | |
| const origPost = ch.postMessage.bind(ch); | |
| ch.postMessage = (msg) => { | |
| if (msg === LOGOUT_EVENT) return; | |
| return origPost(msg); | |
| }; | |
| const origAdd = ch.addEventListener.bind(ch); | |
| ch.addEventListener = (type, listener, options) => { | |
| if (type !== 'message' || typeof listener !== 'function') { | |
| return origAdd(type, listener, options); | |
| } | |
| const wrapped = function (ev) { | |
| if (ev && ev.data === LOGOUT_EVENT) return; | |
| return listener.call(this, ev); | |
| }; | |
| return origAdd(type, wrapped, options); | |
| }; | |
| let onMessageHandler = null; | |
| Object.defineProperty(ch, 'onmessage', { | |
| configurable: true, | |
| enumerable: true, | |
| get() { | |
| return onMessageHandler; | |
| }, | |
| set(fn) { | |
| onMessageHandler = fn; | |
| if (typeof fn !== 'function') return; | |
| origAdd('message', function (ev) { | |
| if (ev && ev.data === LOGOUT_EVENT) return; | |
| fn.call(ch, ev); | |
| }); | |
| } | |
| }); | |
| } | |
| return ch; | |
| }; | |
| window.BroadcastChannel.prototype = NativeBC.prototype; | |
| } | |
| // 2) Block redirects to /oauth/logout (unless manually allowed). | |
| const locProto = Location.prototype; | |
| const origAssign = locProto.assign; | |
| const origReplace = locProto.replace; | |
| locProto.assign = function (url) { | |
| if (shouldBlockLogoutUrl(url)) return; | |
| return origAssign.call(this, url); | |
| }; | |
| locProto.replace = function (url) { | |
| if (shouldBlockLogoutUrl(url)) return; | |
| return origReplace.call(this, url); | |
| }; | |
| try { | |
| const hrefDesc = Object.getOwnPropertyDescriptor(locProto, 'href'); | |
| if (hrefDesc && hrefDesc.get && hrefDesc.set) { | |
| Object.defineProperty(locProto, 'href', { | |
| configurable: true, | |
| enumerable: hrefDesc.enumerable, | |
| get() { | |
| return hrefDesc.get.call(this); | |
| }, | |
| set(url) { | |
| if (shouldBlockLogoutUrl(url)) return; | |
| return hrefDesc.set.call(this, url); | |
| } | |
| }); | |
| } | |
| } catch (_) { | |
| // Some browsers may not allow redefining Location.href | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment