El token caduca de madrugada: quién se entera y qué pasa
Casi ningún permiso dura para siempre y el que caduca no avisa a nadie. Cómo se diseña la renovación para que no desconecte cuentas, con relojes reales.
Casi ningún permiso dura para siempre, y el que caduca no avisa a nadie. No hay un aviso previo del proveedor, no hay un correo, no hay una notificación: simplemente, la siguiente llamada falla. Y la siguiente llamada suele ser un trabajo automático a las tres de la mañana, cuando no hay nadie delante.
Esto es lo que se aprende manteniendo la renovación de permisos de todas las redes sociales grandes a la vez, y es la parte de una integración que peor se presupuesta porque no se ve en la demostración.
Cada proveedor tiene su reloj, y no se parecen
La primera suposición que hay que tirar es que existe «el tiempo de caducidad». Conviven estos, todos en producción y todos a la vez:
| Duración del token de acceso | Proveedores |
|---|---|
| 60 días | Meta (Facebook, Instagram), LinkedIn, Threads |
| 24 horas | TikTok |
| 60 minutos | Google (YouTube, Google Business) |
| Menos de 30 minutos | Bluesky |
| No caduca | Webhook de Discord, token de bot de Slack, Telegram (no hay token de cuenta) |
Y la duración no es lo único que cambia. También cambia qué se canjea: hay proveedores que devuelven un refresh token nuevo cada vez, otros que lo entregan una sola vez y para siempre, y alguno que no tiene refresh token en absoluto.
De aquí sale la primera decisión de diseño: no hay «la tarea de renovar tokens». Hay dos caminos que conviven.
Un barrido programado no basta
La forma evidente es una tarea que cada hora recoja las cuentas que caducan pronto y las renueve. Funciona con tokens de 60 días. Con tokens de 60 minutos, una cuenta puede caducar entre dos pasadas, y el trabajo que publica corre cada minuto, no cada hora.
Así que hace falta lo otro: renovar bajo demanda, justo antes de usar el token, cuando le queda poco. Y entonces los dos caminos conviven y tienen que ser idempotentes: el que llegue primero deja el token fresco y el otro no hace nada.
Aun así el barrido no sobra, y el motivo es el que más cuesta ver: una cuenta que nadie use en semanas no pasa nunca por el camino de la demanda. Sin barrido, la sesión se muere sola sin que ninguna llamada falle, porque no hay ninguna llamada.
El caso extremo de esto es un proveedor con token de 60 días y sin recuperación posible: una cuenta conectada y dejada quieta 61 días se cae sola, y volver a conectarla es cosa del usuario. Ahí el barrido no es una optimización, es lo único que mantiene viva la cuenta.
El refresh token de un solo uso
Hay protocolos en los que el refresh token rota: al canjearlo te dan uno nuevo y el anterior queda quemado. Es más seguro y rompe el diseño ingenuo, porque convierte un problema de OAuth en un problema de concurrencia.
Si la tarea programada y la aplicación renuevan la misma cuenta a la vez, una de las dos canjea un token ya gastado. El proveedor contesta que no vale, y como esa respuesta es indistinguible de un permiso revocado, la cuenta acaba marcada como caída. La cuenta se desconecta sola, sin que nadie haya hecho nada mal.
Dos cosas lo arreglan, y hacen falta las dos:
- Un cerrojo por cuenta. Solo un proceso renueva esa cuenta a la vez; el otro espera y se encuentra el token ya fresco. La tarea programada no canjea por su cuenta: pide la renovación por el mismo camino que todos.
- Escritura condicionada al token con el que se entró. Al guardar el resultado, se exige que en la base de datos siga estando el token que se canjeó. Si ya no está, es que otro lo rotó mientras tanto y lo guardado es más nuevo: no se pisa. Sin esa condición, un proceso lento devuelve a la base de datos un token gastado y la cuenta muere en la pasada siguiente.
La segunda es la que se olvida, porque el cerrojo parece suficiente. No lo es cuando el otro proceso tiene la copia vieja en memoria desde antes de que el cerrojo existiera.
No pises lo que el proveedor no te ha dado
Este es un fallo de una línea que mata cuentas por redes enteras.
Lo normal es que la respuesta de la renovación traiga token nuevo, refresh token nuevo y caducidades, así que se asignan los tres. Y entonces llega un proveedor que no devuelve refresh token, porque lo entregó una sola vez en el alta y no caduca (Google), o porque no existe y lo que se presenta para renovar es el propio token de acceso (Threads).
Asignar a ciegas la respuesta deja el campo vacío. La cuenta sigue funcionando hasta la siguiente renovación, que ya no tiene con qué renovar. En la práctica, la primera pasada de la tarea programada mata todas las cuentas de esa red, y el cliente se las encuentra desconectadas sin saber por qué.
La regla es corta: solo se pisa lo que la red haya devuelto de verdad. Y si el lenguaje lo permite, que el campo sea opcional en el tipo de la respuesta, para que sea el compilador quien avise el día que alguien quite la comprobación.
El fallo pasajero no puede desconectar a nadie
Cuando una renovación falla hay que decidir si la cuenta se marca o no, y las dos decisiones cuestan dinero si se toman mal.
Marcarla la saca del ciclo automático, que es exactamente lo que se quiere con un permiso revocado: reintentar cada hora contra un token que ya no sirve no arregla nada y gasta cuota. Pero si el fallo era un tiempo de espera agotado o un 500 del proveedor, esa cuenta se queda en rojo hasta que el usuario reconecte algo que no tenía nada roto.
Así que antes de marcar hay que separar el fallo pasajero del definitivo, y guardar el código concreto, no uno genérico para todo. Es lo que le permite al panel decir qué hacer: no es lo mismo «vuelve a conectar esta cuenta» que «el proveedor está caído, no hagas nada».
Quién se entera
Reconectar solo lo puede hacer el usuario. Ninguna capacidad técnica arregla eso, así que el diseño se juzga por cómo llega el aviso hasta él:
- En el panel, en la cuenta concreta, diciendo qué pasa y qué hacer. Un icono rojo sin frase obliga a abrir una incidencia.
- Por webhook firmado hacia la aplicación del cliente, si quien integra es otro producto. Su interfaz es la que ve su usuario, y sin ese aviso no puede pedirle nada.
- Sin ruido cuando no hay nada que hacer. Un aviso por cada fallo pasajero enseña a la gente a ignorarlos.
Lo que hay que preguntar antes de presupuestar
- ¿Cuánto dura el permiso de cada proveedor, y hay alguno que no se pueda recuperar?
- ¿El refresh token rota? Si rota, ¿quién tiene el cerrojo?
- ¿Qué pasa con una cuenta que nadie usa en dos meses?
- ¿Cómo se entera el usuario, y cuánto tarda en enterarse?
Ninguna de las cuatro se contesta con la documentación del proveedor delante: se contestan mirando cómo está hecho el sistema. Es parte de lo que incluye nuestro servicio de integraciones y APIs, y de por qué mantener una integración cuesta después de entregarla.
Preguntas frecuentes
- ¿Cuánto dura un token de acceso de OAuth?
- No hay una respuesta única, y esa es la parte que rompe los diseños. Entre los proveedores que mantenemos conviven tokens de 60 días (Meta, LinkedIn, Threads), de 24 horas (TikTok), de 60 minutos (Google, y con él YouTube y Google Business), de menos de 30 minutos (Bluesky) y permisos que no caducan nunca (el webhook de Discord o el token de bot de Slack). Un sistema que asuma un solo ritmo se cae con el primer proveedor que no lo cumpla.
- ¿Basta con una tarea programada que renueve los tokens?
- Solo si el token dura mucho más que el intervalo de la tarea. Con tokens de 60 minutos y una pasada cada hora, hay cuentas que llegan caducadas a la siguiente llamada, así que hace falta además renovar bajo demanda antes de usar el token. Los dos caminos tienen que ser idempotentes: el que llegue primero deja el token fresco y el otro no hace nada.
- ¿Por qué se desconecta una cuenta sola si el token todavía servía?
- Casi siempre por una renovación doble. Cuando el refresh token es de un solo uso y rota en cada canje (es el caso del protocolo de Bluesky), si la tarea programada y la propia aplicación renuevan a la vez, uno de los dos canjea un token ya gastado, el proveedor contesta que no vale y la cuenta queda marcada como caída sin que nadie haya hecho nada mal. Se arregla con un cerrojo por cuenta y escribiendo solo si en la base de datos sigue estando el token con el que se entró.
- ¿Qué tiene que ver el usuario con todo esto?
- Es el único que puede arreglarlo cuando falla de verdad. Un permiso revocado o caducado sin recuperación no se resuelve reintentando: hace falta que la persona vuelva a autorizar. Por eso el fallo tiene que llegar hasta ella con un mensaje que diga qué cuenta es y qué tiene que hacer, y por eso conviene distinguir el fallo pasajero del definitivo antes de asustar a nadie.