Imagen ilustrativa: Unsplash
Activaste App Check, pulsaste aplicar, y ahora Firestore devuelve permission-denied con el mensaje Missing or insufficient permissions. Ese mensaje no te dice nada, porque es exactamente el mismo que devuelve un fallo de reglas de seguridad. No puedes diagnosticarlo leyendo el error. Tienes que abrir la consola de Firebase, entrar en la sección de métricas de App Check y mirar en qué categoría están cayendo tus peticiones. Ahí está la respuesta, y ahí es donde casi nadie mira antes de pasar dos horas revisando reglas que estaban bien.
Lo primero: la aplicación tarda quince minutos en propagarse
Antes de tocar código. La documentación oficial dice que puede tardar hasta quince minutos desde que activas la aplicación hasta que surte efecto. Lo que casi nadie menciona es que el retardo funciona en los dos sentidos: si acabas de desactivar la aplicación porque se te cayó la app en producción, las peticiones pueden seguir fallando un rato. Si desactivaste y sigue fallando, espera antes de concluir que el problema es otro.
Las cinco categorías de la consola y qué significa cada una
La sección de métricas clasifica cada petición en una de estas cinco categorías. Aprenderlas es la mitad del diagnóstico:
- Verified. Peticiones con un token válido. Cuando actives la aplicación, solo estas pasarán.
- Outdated client. Peticiones sin token que parecen venir del SDK de Firebase. Traducción: usuarios con una versión vieja de tu app, anterior a que añadieras App Check. Si esta categoría tiene volumen, no actives la aplicación todavía, porque vas a bloquear a usuarios reales.
- Unknown origin. Peticiones sin token que no parecen venir del SDK. Aquí es donde aparecen las claves de API robadas y las peticiones forjadas. Es la categoría que justifica que App Check exista.
- Invalid. Peticiones con un token no válido. La documentación incluye en esta categoría, explícitamente, los entornos emulados. Si estás probando en un emulador sin token de depuración registrado, tus peticiones caen aquí.
- Reused token. Peticiones con un token ya usado antes. Solo aplica si activaste la protección contra repetición.
Sobre cuándo activar la aplicación, la recomendación oficial no es una cantidad de días, es una proporción: actívala cuando casi todas las peticiones recientes sean de clientes verificados, y retrásala si una parte significativa viene de clientes probablemente desactualizados. Para una app que aún no has publicado, actívala de inmediato.
Los proveedores de atestación y su estado real
Aquí hay una trampa documental que conviene conocer. La página oficial de Flutter todavía menciona Safety Net como opción de Android, pero SafetyNet se apagó por completo en enero de 2025 y el enum AndroidProvider del paquete ya no lo incluye. Si heredaste código que referencia AndroidProvider.safetyNet, no va a compilar.
| Proveedor | Plataforma | Estado | TTL por defecto | Dónde falla |
|---|---|---|---|---|
| Play Integrity | Android | Activo, es el único de Android | 1 hora | APK instalado fuera de Google Play, firma que no coincide, cuota diaria agotada |
| SafetyNet | Android | Apagado en enero de 2025 | No aplica | Siempre. Devuelve error de red |
| App Attest | iOS 14 o superior | Activo, requiere Xcode 12.5 o superior | 1 hora | Entitlement apuntando al entorno sandbox, que App Check no acepta |
| DeviceCheck | Apple | Activo, sirve de alternativa por debajo de iOS 14 | 1 hora | Clave privada de Apple ausente o mal configurada |
| reCAPTCHA Enterprise | Web | Recomendado para integraciones nuevas | 1 hora | Dominio no autorizado. En plan Spark solo hay cuatro niveles de puntuación |
| reCAPTCHA v3 | Web | Sigue funcionando, desaconsejado para proyectos nuevos | 1 día | Permitir localhost en los dominios de reCAPTCHA, que la documentación desaconseja |
Una nota sobre el TTL que afecta a la factura y a la estabilidad: la librería refresca el token a la mitad de su duración, y el aviso oficial es directo. Un TTL corto agota la cuota más rápido y, en servicios de pago, cuesta más. El mínimo permitido son treinta minutos, lo que duplica las atestaciones frente al valor por defecto de una hora.
El fallo asimétrico que desconcierta a todo el mundo
Un patrón que veo repetirse: Firestore falla pero las Cloud Functions siguen respondiendo, o al revés. No es un misterio, es que la aplicación se activa en dos sitios distintos. Los productos que se controlan desde la consola son Firebase AI Logic, SQL Connect, Realtime Database, Firestore, Storage, Authentication y las APIs de Maps. Cloud Functions no está en esa lista porque su aplicación se activa en el código de la función, con la opción enforceAppCheck, y requiere volver a desplegar. Además solo cubre funciones invocables, no funciones HTTP genéricas.
Traducción práctica: si activaste App Check en la consola y tus funciones siguen aceptando peticiones sin token, no está roto. Es que nunca lo activaste para ellas.
El orden de inicialización en Flutter
La documentación es explícita: activate() va después de Firebase.initializeApp() y antes de usar cualquier servicio de Firebase. Los dos con await. Este es el orden correcto:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await FirebaseAppCheck.instance.activate(
androidProvider: AndroidProvider.debug,
appleProvider: AppleProvider.appAttest,
);
runApp(App());
}
Si lanzas peticiones a Firestore o Storage antes de que activate() termine, el SDK puede acabar usando un token de marcador de posición, y el backend lo rechaza. El síntoma que reportan los desarrolladores en el repositorio de FlutterFire es un 401 con el cuerpo Firebase App Check token is invalid, precedido en los logs por un aviso de que no se pudo obtener el token y se usó uno de relleno.
Cómo trabajar en emulador sin desactivar la protección
App Check no forma parte del conjunto de emuladores locales de Firebase, así que un emulador sin preparar cae en la categoría Invalid. La solución documentada es el proveedor de depuración, no relajar la configuración.
- Configura el proveedor de depuración en la plataforma correspondiente. En Flutter,
AndroidProvider.debugoAppleProvider.debug. - Ejecuta la app y busca el token en los logs. En Android el mensaje pide que introduzcas ese secreto en la lista de permitidos de la consola. En iOS aparece como App Check debug token, y para verlo hay que añadir
-FIRDebugEnableda los argumentos del esquema en Xcode. En web se activa poniendoself.FIREBASE_APPCHECK_DEBUG_TOKEN = true;antes de inicializar, y el token sale por la consola del navegador. - Registra el token en la consola de Firebase: Seguridad, App Check, pestaña Apps, menú de tres puntos de la app, Manage debug tokens.
- Para regenerar un token, desinstala y reinstala la app. Eso limpia el almacenamiento local.
- En integración continua, pásalo por variable de entorno en lugar de dejarlo en el código.
Las advertencias oficiales sobre el token de depuración merecen leerse dos veces: un token válido y una build de depuración dan acceso a tus servicios desde dispositivos no verificados. No lo subas a un repositorio público ni lo incluyas en builds de producción, y si se compromete, bórralo en la consola inmediatamente.
Play Integrity y las builds que nunca van a pasar
Si distribuyes por Google Play, la configuración recomendada exige los veredictos de aplicación reconocida y con licencia. Una app instalada por sideload o un APK de depuración no obtiene ninguno de los dos: Android devuelve UNLICENSED y UNRECOGNIZED_VERSION. Esas instalaciones fallarán la atestación en modo aplicado, y es el comportamiento esperado.
Dos requisitos de configuración que fallan a menudo. El primero es la huella SHA-256: tiene que ser la de la clave con la que Google Play distribuye la app, que no es la de tu keystore de subida si usas Play App Signing. El segundo es vincular el proyecto de Google Cloud desde Play Console, en la sección de integridad de la aplicación, y ser propietario del proyecto para poder hacerlo.
Sobre carga: Play Integrity tiene una cuota por defecto de diez mil peticiones diarias para todas las instalaciones sumadas. Al agotarla devuelve TOO_MANY_REQUESTS, con código -8. La resolución oficial es reintentar con retroceso exponencial y solicitar un aumento, para el que tu app debe estar disponible en Google Play y cuya tramitación puede llevar una semana.
Veredicto crítico
App Check hace lo que promete. Si tienes claves de API de Firebase expuestas en un cliente web o en un APK, que alguien las extraiga es cuestión de tiempo, y App Check es la única capa que distingue tu app de un script con tus credenciales. Para cualquier proyecto con backend de Firebase y usuarios reales, activarlo no es opcional.
Mi objeción es a la ergonomía del diagnóstico, y es la misma que le hago a otros productos de Google: el error que llega al cliente no dice la verdad. Que Firestore devuelva el mismo permission-denied para un fallo de reglas y para un token de atestación rechazado obliga a salir de la aplicación y entrar en una consola web para saber qué pasó. Eso está bien en desarrollo y es malísimo a las tres de la mañana con producción caída. Y la documentación arrastra inconsistencias: la página de Flutter sigue ofreciendo un proveedor apagado hace más de un año.
Cuándo activar la aplicación de inmediato: apps que aún no has publicado, o backends nuevos sin base instalada. Cuándo esperar: si tienes usuarios con versiones antiguas, porque la categoría de clientes desactualizados te dice exactamente a cuántos vas a dejar fuera. Y una recomendación que no está en la documentación pero que ahorra disgustos: activa App Check en modo de solo monitorización desde el primer día del proyecto, aunque no vayas a aplicarlo hasta meses después. Los datos que recoges mientras tanto son los que te dicen cuándo es seguro apretar el botón.
Sigue leyendo
- Google Play rechaza tu app Flutter por seguridad de los datos, el otro muro que te encuentras al publicar una app con Firebase.
- Error 429 en la API de Gemini, el mismo problema de fondo: un error genérico que oculta la causa real.
- Servidor MCP configurado pero no aparece en el cliente, diagnóstico paso a paso cuando la herramienta no carga.
- El resto está en el índice de guías prácticas.
Fuentes
Visión general de App Check y servicios soportados: firebase.google.com/docs/app-check. Activación de la aplicación y retardo de propagación: enable-enforcement. Categorías de las métricas de la consola: monitor-metrics. Proveedor Play Integrity, huella SHA-256 y veredictos por canal de distribución: play-integrity-provider. Proveedores por defecto e inicialización en Flutter: flutter/default-providers. Proveedor de depuración en Flutter: flutter/debug-provider. Aplicación en Cloud Functions: cloud-functions. Apagado de SafetyNet: developer.android.com. Cuotas y códigos de error de Play Integrity: integrity/error-codes.
No te pierdas lo próximo que publiquemos
Cada día, las noticias y guías de inteligencia artificial que importan, directas a tu correo.
Es gratis. Sin spam. Puedes darte de baja cuando quieras.