Block Google One Tap

Block annoying and glitchy Google One Tap prompts

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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