---
title: "FedCM миграция: AbortError, два элемента и 23 промпта с Claude Code"
description: "История отладки Google One Tap авторизации после миграции на FedCM - как AbortError привёл к обнаружению дублирующих google-auth элементов за 23 промпта с Claude Code"
url: "https://ifonin.ru/blog/fedcm-migration-aborterror-debugging-claude-code/"
date: 2026-03-03
tags: ["google-auth","fedcm","debugging","claude-code","react"]
---

# FedCM миграция: AbortError, два элемента и 23 промпта с Claude Code

Авторизация через Google One Tap перестала работать в incognito. Ошибка выглядела как баг FedCM, а оказалась - два google-auth элемента на одной странице.

## Что произошло

В taskboard-pro авторизация через Google One Tap была критична для конверсии. Пользователь открывает приложение, видит всплывающий промпт со своим гугл-аккаунтом, кликает - и сразу внутри. Без паролей. Это работало - до тех пор, пока Google не начала принудительный перевод трафика на FedCM.

Перевод шёл постепенно: сначала часть пользователей, потом больше. Причина - браузеры убирают поддержку сторонних cookies, и старый Google Sign-In держался именно на них. FedCM заменяет это на уровне платформы: браузер сам медиирует между сайтом и Google, без cookies и редиректов.

Миграция растянулась у меня на несколько часов. Начало было обманчивым - на обычных вкладках всё работало. Потом я открыл incognito и увидел тишину: никакого промпта, никакой ошибки в UI. Только в консоли:

```
[GSI_LOGGER]: FedCM get() rejects with AbortError: The request has been aborted.
```

Первая мысль - cooldown период. FedCM ввёл тихий период между попытками автоматической авторизации: если пользователь закрыл промпт крестиком, браузер запоминает и блокирует повторные вызовы на некоторое время. Но в incognito - судя по поведению - никакой истории нет, cooldown не накапливается. Значит, причина в другом.

## Что такое FedCM и зачем мигрировать

FedCM - Federated Credential Management - спецификация, находящаяся в статусе First Public Working Draft в W3C с августа 2024 года, но браузеры уже её реализуют. Она позволяет Identity Provider-у (Google, в нашем случае) авторизовывать пользователей без third-party cookies и редиректов. Браузер становится посредником: он знает, что вы залогинены в Google, и передаёт этот факт приложению через нативный UI.

Для разработчика это выглядит просто: добавляешь флаг `use_fedcm_for_prompt: true` при инициализации Google Identity Services - и всё должно работать. Я так и думал, пока не столкнулся с AbortError.

На практике FedCM оказался строже старого Google Sign-In. Старый GSI прощал многое - несколько вызовов `prompt()`, дублирующие элементы инициализации. С FedCM я обнаружил другое: concurrent-запросы давали AbortError, два элемента инициализации на странице - тоже. Похоже, браузер допускает только один активный credential request в момент времени, хотя в документации я явного подтверждения этому не нашёл. Дедлайн полной миграции был в августе 2025 - кто не успел к тому моменту, начал терять авторизацию в incognito почти везде.

## Баг: AbortError и два элемента

Первые несколько попыток отладки были классическими: проверить сеть, проверить конфигурацию в Google Cloud Console, убедиться что Client ID правильный. Приличное время я провёл в DevTools, глядя на одни и те же запросы. Всё выглядело нормально.

Примерно на 8-м промпте с Claude Code я спросил напрямую: "объясни все возможные причины AbortError при FedCM в incognito". Claude Code перечислил сценарии: cooldown, concurrent requests, duplicate elements. И сразу пометил: в incognito cooldown маловероятен - значит, смотреть на дубликаты.

Это был поворотный момент. На 9-м промпте я попросил: "найди в проекте все места, где используется google.accounts.id". Я бы листал файлы по одному, держа контекст в голове. Claude Code прошёлся по всему codebase и выдал список сразу.

Два элемента. В `Layout.jsx` был `<div id="g_id_onload">` с атрибутом `data-use_fedcm_for_prompt="true"` - добавили при миграции. И в `Login.jsx` был ещё один такой же `<div id="g_id_onload">` - старый, оставшийся с предыдущей реализации. Плюс в `Login.jsx` - явный вызов `google.accounts.id.initialize()` с последующим `google.accounts.id.prompt()`.

Три попытки создать FedCM credential request при каждой загрузке страницы авторизации. Убрал дубликат из `Login.jsx` - ошибка ушла. Старый GSI молча проглатывал такое - показывал один промпт. Именно поэтому баг появился только после миграции, хотя дублирующие элементы существовали раньше.

Промпты с 10-го по 15-й ушли на архитектуру исправления. С 16-го по 23-й - на проверку: "нет ли других мест в коде, где google.accounts.id.prompt вызывается напрямую". Оказалось, был ещё один вызов в компоненте с кнопкой Sign In. Не виновник изначального бага, но потенциальный источник конфликта в будущем.

Решение - единая точка инициализации:

```javascript
// Было: инициализация в нескольких местах
// Login.jsx
useEffect(() => {
  google.accounts.id.initialize({
    client_id: CLIENT_ID,
    callback: handleCredentialResponse,
    use_fedcm_for_prompt: true
  });
  google.accounts.id.prompt();
}, []);
```

```javascript
// Стало: один модуль, один флаг
// auth.js
let googleInitialized = false;

export function initGoogleIdentity(callback) {
  if (googleInitialized) return;

  google.accounts.id.initialize({
    client_id: CLIENT_ID,
    callback,
    use_fedcm_for_prompt: true,
    auto_select: false
  });

  googleInitialized = true;
}
```

```javascript
// App.jsx - единственное место вызова
useEffect(() => {
  initGoogleIdentity(handleCredentialResponse);
  google.accounts.id.prompt();
}, []);
```

HTML-элемент `g_id_onload` остался только в корневом Layout, дублирующий JS-вызов убран.

Работает. В incognito тоже.

## Как Claude Code помогал отлаживать

23 промпта от первой ошибки до финального фикса - уложился за одну сессию. Большая часть была не про написание кода, а про понимание: почему именно этот сценарий, что ещё могло быть виновато, где ещё смотреть.

Я мог бы часами смотреть в `Login.jsx` и не найти проблему - потому что проблема была не там. Она была в том, как `Login.jsx` взаимодействовал с `Layout.jsx` через глобальный скрипт Google. Claude Code увидел оба файла сразу и объяснил конфликт. Мне было бы легко зациклиться на одном компоненте и пропустить связи между ними.

Главное преимущество AI-ассистента при отладке - он держит весь codebase в голове одновременно. Не отдельный компонент, а зависимости между компонентами. При классическом дебаге ты смотришь в один файл, переключаешься на другой, держишь контекст сам - и когда файлов много, связи теряются.

## Выводы

FedCM строже старого Google Sign-In - это не баг, это фича. Браузер стал активным участником авторизации, и он не прощает архитектурных ошибок, которые раньше проглатывались молча.

Правило первое при FedCM: одна инициализация Google Identity на всё приложение. Никаких `google.accounts.id.initialize()` в компонентах, которые монтируются и анмонтируются. Один модуль, один флаг, один вызов при старте.

Правило второе: при миграции ищи дубликаты. Все `g_id_onload` в HTML, все вызовы `initialize` в JS. FedCM найдёт то, что старый GSI игнорировал.

Теперь при любой крупной миграции первым делом прошу Claude Code найти все точки инициализации нового API по всему проекту. Не потому что лень - а потому что cross-file поиск с объяснением зависимостей это именно то, что AI-ассистент делает быстрее при ручном дебаге.

AbortError оказался не ошибкой FedCM. Он был симптомом архитектуры, которая работала по случайности.
