Skip to content
This page has been auto-translated and may contain errors.View in English

Cómo hacen las apps para recordarte

Inicias sesión. Haces clic en algo. El servidor no tiene idea de quién eres.

HTTP no guarda estado: cada solicitud llega desde cero, sin memoria de la anterior y sin ninguna forma incorporada de conectar una con otra. El login que funcionó hace un segundo no dejó ningún rastro.

Por eso, una app que necesita reconocerte entre solicitudes tiene que guardar tu identidad en algún lado y recuperarla en cada solicitud que llegue después.

Todo este capítulo gira en torno a una sola pregunta: ¿dónde vive la identidad?

Casi todos los flujos de login en la web responden esto de una de tres formas.

El servidor recuerda

En la identidad con estado, el servidor es quien guarda la información.

Cuando inicias sesión, el servidor crea una sesión, un pequeño paquete de datos que te representa: un id de usuario, un nombre, tal vez un rol. Esa sesión vive en el servidor. El navegador recibe una cookie con un id de sesión, nada más, y la envía automáticamente en cada solicitud a ese dominio. El servidor toma el id, busca la sesión, y así vuelve a saber quién eres.

Este es el patrón tradicional de las apps web, y el predeterminado en muchos frameworks. Si alguna vez usaste express-session, esto es exactamente lo que hacía.

JunoEl servidor recuerda Lo que vale la pena quedarte es cuán poco se le confía al navegador. Recibe un id y nada más.

Todo lo que importa sobre ti se queda en el servidor, y el id solo le sirve a alguien que también pueda llegar hasta ese servidor.

JunoEl servidor recuerda La consulta en cada solicitud es lo que hay que notar, porque es a la vez la fortaleza y el costo de este modelo. Significa que el servidor puede modificar o eliminar una sesión en cualquier momento, y la siguiente solicitud lo siente de inmediato.

También significa que cada solicitud toca un almacenamiento compartido. En un solo servidor eso es memoria y no cuesta nada. En varios servidores es un almacén al que todos tienen que llegar, que es la primera pieza de infraestructura que este modelo te obliga a mantener.

JunoEl servidor recuerda La revocación instantánea es la propiedad que estás comprando, y vale más de lo que parece. Despedir a alguien, cambiarle el rol, reaccionar a una laptop robada: eliminas la sesión y la siguiente solicitud queda anónima. No hay que esperar a que nada expire.

Lo que pagas a cambio es un problema de coordinación que crece con tu infraestructura. Las sesiones en memoria del proceso mueren con ese proceso y no existen para la instancia de al lado, así que cualquier escalamiento horizontal necesita un almacén compartido, y ese almacén se convierte en algo que tiene que seguir funcionando para que cualquiera pueda seguir con sesión iniciada.

Las sesiones persistentes (sticky sessions) son el atajo tentador, y cambian el problema por uno peor: tu balanceo de carga ahora queda atado a tu autenticación, y perder una instancia cierra la sesión de todos a los que atendía.

El cliente lleva la prueba

En la identidad sin estado, el servidor no guarda nada sobre ti.

El cliente tiene un token y lo envía en cada solicitud. El servidor verifica el token y responde, sin tener que consultar nada sobre quién eres entre una solicitud y otra.

Esto funciona bien para APIs, apps móviles y sistemas distribuidos, donde compartir memoria entre servidores resulta incómodo o simplemente no es posible. Si alguna vez llamaste a una API y recibiste 401 Unauthorized: missing authorization header, ese es este modelo diciéndote que traigas tu prueba.

Hay dos versiones de esto, y la diferencia es tan importante que cada una tiene su propio capítulo. Un token opaco de portador es una cadena aleatoria sin significado que el servidor tiene que consultar. Un JSON Web Token lleva la identidad dentro de sí mismo, firmada, así que no hace falta ninguna consulta.

JunoEl cliente lleva la prueba El nombre confunde un poco al principio. El servidor no guarda estado sobre ti, no sobre todo lo demás.

Igual tiene una base de datos llena de usuarios. Lo que deja de mantener es un registro del hecho de que en este momento tienes la sesión iniciada.

JunoEl cliente lleva la prueba "Portador" (bearer) es la palabra clave en "bearer token", y significa exactamente eso: quien lo porta, lo posee. El token no está atado a un dispositivo, un navegador o una red, así que un token copiado es un token que funciona.

Por eso el transporte y el almacenamiento importan tanto acá. HTTPS en todas partes, y pensarlo bien a la hora de decidir dónde lo guarda el cliente, porque cualquier cosa legible por JavaScript también es legible por JavaScript inyectado.

JunoEl cliente lleva la prueba El compromiso es exactamente el inverso al de las sesiones. Ganas un servidor que no necesita estado compartido y escala horizontalmente sin costo, y a cambio pierdes la capacidad de cambiar de opinión.

Un token que firmaste es válido hasta que expira, esté donde esté, pase lo que pase mientras tanto. No hay ninguna lista de la que borrarlo. Por eso el diseño habitual en producción es un token de acceso de corta duración junto con un token de renovación (refresh token) de mayor duración que sí puede revocarse: eso reintroduce una consulta, pero solo al renovar, no en cada solicitud.

Vale la pena decirlo con claridad. La identidad completamente sin estado renuncia a la revocación, y cualquier sistema que diga ofrecer ambas cosas en realidad volvió a meter algo de estado en alguna parte.

Alguien más responde por ti

En la identidad delegada, un tercero en el que confías confirma quién es el usuario en tu nombre.

Tu app le pregunta a Google si reconoce a esta persona. Google se encarga del login y le entrega a tu app un token que prueba la respuesta. Cada botón de "Iniciar sesión con Google", "Iniciar sesión con GitHub" o "Iniciar sesión con Apple" es exactamente esto.

Lo atractivo es que dejas de operar un sistema de contraseñas. Sin almacenamiento de contraseñas, sin flujo de recuperación, sin filtración de credenciales que nunca tuviste. OAuth e identidad delegada explica cómo funciona realmente esta transferencia.

JunoAlguien más responde por ti Piénsalo como que alguien responde por ti. No te pruebas directamente ante la app. Alguien en quien la app ya confía te confirma, y la app le cree.

Por eso te redirige a una página de Google y luego te trae de vuelta. La verificación pasó allá.

JunoAlguien más responde por ti Elimina una categoría de riesgo y agrega una dependencia. Tu login ahora deja de funcionar cuando el de ellos falla, y los usuarios que no tengan cuenta ahí directamente no pueden registrarse.

Por eso la mayoría de las apps de consumo lo ofrecen junto con email y contraseña en lugar de reemplazarlo, y después tienen que lidiar con que la misma persona llegue por ambas puertas.

JunoAlguien más responde por ti Vale la pena ser precisos, porque el vocabulario acá se usa mal todo el tiempo. OAuth otorga autorización delegada: permiso para que tu app actúe sobre un recurso. OpenID Connect es la capa de identidad construida encima, y los botones de "iniciar sesión con" son OIDC. Usar un token de acceso de OAuth como prueba de quién es alguien, en lugar de un token de identidad de OIDC, es un error real y bastante común.

Lo que heredas son las decisiones del proveedor. La duración de sus sesiones, su recuperación de cuentas, su definición de lo que significa una dirección de email. Si ellos permiten recuperar una cuenta por número de teléfono, eso ahora también es parte de la recuperación de tu cuenta.

Vincular cuentas es el punto más delicado. Emparejar un login delegado con una cuenta existente usando el email es lo primero a lo que todo el mundo recurre, y solo es seguro cuando el proveedor verifica ese email, cosa que no todos hacen.

Uno al lado del otro

Con estadoSin estadoDelegado
Dónde vive la identidadEn el servidorEn el token que tiene el clienteCon el proveedor
Costo por solicitudUna consultaUna verificación de firma, o una consultaDepende del token que termines usando
Revocar el accesoInmediatoDifícil, normalmente hay que esperar a que expireEn parte, depende del proveedor
Escala horizontalmenteNecesita un almacén compartidoLibrementeLibremente
Lo principal que sacrificasCoordinación entre servidoresLa capacidad de cambiar de opiniónControl e independencia

Ninguno de los tres es la respuesta correcta. Cada uno optimiza para cosas distintas, por eso los sistemas reales suelen combinar más de uno: un login delegado para establecer quién eres, una sesión para el sitio web, tokens para la API.

JunoUno al lado del otro No intentes memorizar esta tabla todavía. Va a tener mucho más sentido una vez que veas cada modelo funcionando.

La idea principal que vale la pena llevarte al siguiente capítulo: con estado es para tener control, sin estado es para escalar, delegado es para pasarle el problema a alguien más.

JunoUno al lado del otro Cuando te encuentres con un sistema que no conoces, la forma más rápida de entenderlo es preguntarte dónde se guarda la identidad. Todo lo demás se deduce a partir de esa respuesta.

Si la guarda el servidor, fíjate en cómo expiran las sesiones y dónde se almacenan. Si la guarda el cliente, fíjate qué pasa si un token se filtra y cómo se puede revocar. Si la guarda un proveedor, fíjate en cómo se vinculan las cuentas.

JunoUno al lado del otro La fila de revocación es la que decide la mayoría de las discusiones reales, y suele descubrirse tarde. Los equipos eligen el modelo sin estado por sus propiedades de escalamiento, lo lanzan a producción, y después llega el primer incidente que exige cerrarle la sesión a una persona específica de inmediato.

La forma honesta de verlo es que estás eligiendo en qué vas a ser malo. El modelo con estado es malo para escalar y bueno para el control. El modelo sin estado es al revés. Cualquier sistema que promocione tener ambas cosas volvió a meter, calladamente, una consulta en algún lado, lo cual es un diseño perfectamente válido, y vale la pena reconocerlo como tal en lugar de tratarlo como una ganancia gratis.

Ponlo a prueba

Para cada sistema, nombra el modelo que esperarías encontrar y la propiedad que lo determina.

  1. Un panel de administración interno donde revocar el acceso de alguien tiene que surtir efecto de inmediato.
  2. Una API pública que atiende a una app móvil con unos cientos de miles de usuarios.
  3. Una app de fotos cuyo registro completo es un botón de "Continuar con Google".
  4. Una arquitectura de microservicios donde una docena de servicios necesitan saber quién los está llamando.
Compara tus respuestas
#ModeloLa propiedad que lo determina
1Con estadoRevocación inmediata. Eliminas la sesión y la siguiente solicitud queda anónima. Nada más ofrece eso.
2Sin estadoNo hay un almacén de sesiones compartido que coordinar a medida que crece el tráfico, y los clientes móviles pueden llevar un token sin problema.
3DelegadoNo hay que construir, almacenar ni exponer a filtraciones un sistema de contraseñas, y el registro es lo más rápido posible.
4Sin estado, específicamente JWTsCada servicio puede verificar el token por sí mismo sin tener que consultar a un servidor central en cada llamada.

El número 1 es el más interesante. Es el sistema más pequeño de la lista y el que menos presión de escalamiento tiene, y aun así usa el modelo que peor escala, porque acá el control importa más que el crecimiento.

Hacia dónde vamos ahora

Tres familias, cada una resuelve el problema de una forma distinta.

Sesiones y cookies desarma la primera en detalle: qué guarda el servidor, qué lleva el navegador, y los errores que convierten un modelo sólido en uno con fugas.