Block Google One Tap

Block annoying and glitchy Google One Tap prompts

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Advertisement:

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

Advertisement:

// ==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 });
})();