Block Google One Tap

Block annoying and glitchy Google One Tap prompts

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

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