Block Google One Tap

Block annoying and glitchy Google One Tap prompts

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Block Google One Tap
// @namespace    https://chuyanliu.github.io/
// @version      2026-5-31
// @description  Block annoying and glitchy Google One Tap prompts
// @license      GNU GPLv3
// @author       Chuyan Liu
// @match        *://*/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const logger = (...args) => console.log(`[one tap blocker]`, ...args);

    // ------------------------ Block FedCM (modern onetap) ------------------------
    // alternatively: chrome://settings/content/federatedIdentityApi
    if (typeof navigator?.credentials?.get === 'function') {
        const originalGet = navigator.credentials.get;
        navigator.credentials.get = function (options) {
            if (options?.identity?.providers?.some?.(p => p?.configURL?.includes?.('accounts.google.com'))) {
                logger('Blocked FedCM One Tap request');
                // standard generic exception returned when user clicks x, tested chrome 148.0.7778.217
                // does not use IdentityCredentialError (https://developer.mozilla.org/en-US/docs/Web/API/IdentityCredentialError)
                return Promise.reject(new DOMException('Error retrieving a token.', 'NetworkError'));
            }
            return originalGet.apply(this, arguments);
        };
    }

    // ------------------------ Block window.google API (websites before 2026) ------------------------
    function makeAPIBlockLogger(name) {
        return function (...args) {
            logger(`Blocked API call: "google.accounts.id.${name}"`, ...args);
        };
    }

    const _googleNSAccountsID = {
        intermediate: {
            verifyParentOrigin: makeAPIBlockLogger('intermediate.verifyParentOrigin'),
            notifyParentResize: makeAPIBlockLogger('intermediate.notifyParentResize'),
            notifyParentClose: makeAPIBlockLogger('intermediate.notifyParentClose'),
            notifyParentDone: makeAPIBlockLogger('intermediate.notifyParentDone'),
            notifyParentTapOutsideMode: makeAPIBlockLogger('intermediate.notifyParentTapOutsideMode'),
        },
        cancel: makeAPIBlockLogger('cancel'),
        disableAutoSelect: makeAPIBlockLogger('disableAutoSelect'),
        initialize: makeAPIBlockLogger('initialize'),
        prompt: function (callback) {
            makeAPIBlockLogger('prompt')();
            if (typeof callback === 'function') {
                setTimeout(() => {
                    try {
                        // standard generic dismissal callback value when user clicks x. NOT TESTED, I only added this after FedCM was implemented
                        callback({
                            getMomentType: () => 'skipped',
                            isDisplayMoment: () => false,
                            isDisplayed: () => false,
                            isNotDisplayed: () => false,
                            getNotDisplayedReason: () => void 0,
                            isSkippedMoment: () => true,
                            getSkippedReason: () => 'user_cancel',
                            isDismissedMoment: () => false,
                            getDismissedReason: () => void 0
                        });
                    } catch (err) {
                        logger('error whilst running callback for prompt() api:', err);
                    }
                }, 1);
            }
        },
        PromptMomentNotification: makeAPIBlockLogger('PromptMomentNotification'),
        renderButton: makeAPIBlockLogger('renderButton'),
        revoke: makeAPIBlockLogger('revoke'),
        storeCredential: makeAPIBlockLogger('storeCredential'),
        setLogLevel: makeAPIBlockLogger('setLogLevel')
    };

    // previous version is bad, this should maintain compatibility
    Object.defineProperty(window, 'google', {
        value: new Proxy(
            {
                // if somehow google props load BEFORE this script loads, then they should remain
                ...window?.google ?? {},
                accounts: new Proxy({}, {
                    ...window?.google?.accounts ?? {},

                    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
                    get(target, property, receiver) {
                        if (property === 'id') {
                            return _googleNSAccountsID;
                        }
                        return Reflect.get(target, property, receiver);
                    },
                    set(target, property, value, receiver) {
                        if (property === 'id') {
                            return true;
                        }
                        return Reflect.set(target, property, value, receiver);
                    },
                    deleteProperty(target, property) {
                        if (property === 'id') {
                            return false;
                        }
                        return Reflect.deleteProperty(target,property);
                    }
                })
            },
            {
                get(target, property, receiver) {
                    return Reflect.get(target, property, receiver);
                },
                set(target, property, value, receiver) {
                    return Reflect.set(target, property, value, receiver);
                }
            }
        ),
        writable: true,
        configurable: false,
        enumerable: true
    });

    // ------------------------ Block Legacy Iframe ------------------------
    new MutationObserver(function (mutations) {
        for (let i = 0; i < mutations.length; i++) {
            const addedNodes = mutations[i].addedNodes;
            for (let j = 0; j < addedNodes.length; j++) {
                const node = addedNodes[j];
                if (node.tagName === 'IFRAME') {
                    if (node.src?.includes?.('accounts.google.com/gsi')) {
                        node.remove();
                        logger('Deleted legacy onetap iframe:', node);
                    }
                }
            }
        }
    }).observe(document.documentElement, { childList: true, subtree: true });
})();