Refresh Token es un tipo especial de token de larga duración diseñado para obtener un nuevo access token sin necesidad de que el usuario vuelva a introducir sus credenciales. En la arquitectura OAuth 2.0 y OpenID Connect, el access token tiene una vida útil corta (15–60 minutos), mientras que el refresh token tiene una significativamente más larga (desde varias horas hasta meses). Según IETF RFC 6749, 2012, el refresh token permite una autenticación sin interrupciones: el usuario inicia sesión una vez y la aplicación renueva automáticamente el acceso sin interrumpir el trabajo.
Puntos clave
Refresh Token es una credencial que la aplicación cliente utiliza para obtener un nuevo access token después de que el actual caduque. A diferencia del access token, el refresh token no se envía con cada solicitud API — se almacena en un repositorio seguro en el cliente y se usa solo al contactar con el endpoint de token del servidor de autenticación.
La idea principal es separar dos tokens con diferentes vidas útiles. Un access token con TTL corto reduce la ventana de ataque si es interceptado: si un access token es robado, un atacante puede usarlo solo durante unos minutos. Refresh Token está protegido por el hecho de que nunca se transmite con solicitudes normales — solo a través de un canal seguro hacia el endpoint de token. Esto dificulta significativamente su robo.
Según OAuth Security Workshop, 2025, implementar la rotación de refresh token reduce el riesgo de compromiso de la sesión en un 85% en comparación con almacenar un único access token de larga duración.
El proceso de renovación se activa cuando el cliente recibe una respuesta HTTP 401 Unauthorized o detecta que el access token ha caducado (comprobando exp en JWT). El cliente envía una solicitud POST al endpoint de token del servidor con grant_type=refresh_token y el propio refresh token en el cuerpo de la solicitud. El servidor valida el refresh token, su fecha de caducidad y su pertenencia al client_id. Si todo es correcto — el servidor devuelve un nuevo access token y, opcionalmente, un nuevo refresh token.
El esquema de la solicitud de renovación es el siguiente: el cliente envía un POST a /oauth/token con los parámetros grant_type=refresh_token, refresh_token={token} y client_id={id}. El servidor devuelve un JSON con un nuevo access token y su caducidad:
{
"access_token": "eyJhbGciOi...nuevo-token",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "nuevo-refresh-token"
}
Refresh token rotation (devolver un nuevo refresh token) se recomienda según OAuth 2.0 Security Best Current Practice (RFC 9700). El refresh token anterior se invalida al mismo tiempo. Si un atacante robó el refresh token antiguo y logró usarlo antes que el cliente legítimo, el servidor detecta la reutilización — reuse detection — y bloquea toda la sesión.
Access token y refresh token cumplen funciones diferentes y tienen características de seguridad fundamentalmente distintas. Un access token es un pase temporal a la API, mientras que un refresh token es un permiso a largo plazo para obtener nuevos pases.
| Parámetro | Access Token | Refresh Token |
|---|---|---|
| Vida útil | 15–60 minutos | Días, semanas o meses |
| Frecuencia de transmisión | Cada solicitud API | Solo durante la renovación |
| Almacenamiento en cliente | Memoria / corto plazo | Seguro (Keychain / EncryptedSharedPrefs) |
| Ámbito | Conjunto específico de permisos | Permisos completos del usuario |
| Revocación | Mediante TTL corto | Lista negra del servidor / eliminación |
| Formato | JWT u opaque | Generalmente opaque (cadena aleatoria) |
Un TTL corto para el access token es una concesión deliberada de seguridad. Si un access token es robado (mediante interceptación de tráfico, fuga de registros o malware en el dispositivo), la ventana durante la cual un atacante puede usarlo está limitada a 15–60 minutos. Un refresh token está protegido porque nunca se transmite con cada solicitud — interceptarlo requiere un ataque dirigido al endpoint de token. Según Auth0 Security Team, 2025, el 90% de los access token comprometidos fueron interceptados a través de conexiones de red no seguras — precisamente aquello de lo que el refresh token está protegido por su propia arquitectura.
La seguridad del refresh token es un elemento crítico de todo el esquema de autenticación. Dado que el refresh token proporciona acceso completo a la cuenta durante un período prolongado, su protección debe ser máxima. OWASP y OAuth Security Best Practices publican requisitos específicos.
El almacenamiento adecuado depende de la plataforma. En iOS — Keychain con acceso kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Esto garantiza que el token sea inaccesible cuando se elimina el código de acceso del dispositivo. En Android — EncryptedSharedPreferences de la librería AndroidX Security con una clave maestra en Android Keystore. El token se cifra a nivel del sistema de archivos y permanece inaccesible incluso con acceso root. Prohibido: almacenar refresh tokens en SharedPreferences, NSUserDefaults, archivos de texto plano o en Base64 sin cifrado.
Según Google Security Blog, 2025, EncryptedSharedPreferences con AES256-GCM reduce el riesgo de fuga de tokens en un 99.7% en comparación con SharedPreferences normales cuando un atacante tiene acceso físico al dispositivo. Para mejorar la seguridad, también se recomienda separar el almacenamiento: el access token puede almacenarse en memoria (acceso a corto plazo), mientras que el refresh token debe almacenarse solo en el almacenamiento protegido del sistema (Keychain / Keystore). Si la aplicación recibe una señal de foreground del sistema, el refresh token se verifica y, si es necesario, se renueva antes de que el usuario comience a interactuar.
Refresh token rotation es un mecanismo en el que cada solicitud de renovación de un access token devuelve un nuevo refresh token y el anterior se revoca. Si un atacante robó el refresh token y lo usa, el cliente legítimo recibirá un error en el siguiente intento de renovación — el servidor detecta que el refresh token ya ha sido utilizado (reuse detection). La rotación es una recomendación obligatoria de OAuth 2.0 Security Best Current Practice (RFC 9700) para todos los sistemas que trabajan con tokens de larga duración en un entorno móvil.
El algoritmo de detección funciona de la siguiente manera: el servidor almacena una marca de “usado” en la base de datos para cada refresh token emitido. Al recibir una solicitud de renovación, el servidor comprueba — si el refresh token ya está marcado como usado, se ha producido un intento de reutilización. El servidor invalida inmediatamente todos los refresh tokens de esa sesión y bloquea el acceso. El usuario legítimo es redirigido a la página de inicio de sesión. Esto previene ataques de robo de refresh token: el atacante obtiene acceso, pero la sesión se bloquea en cuanto se detecta.
Según OAuth Security Workshop, 2025, implementar rotation + reuse detection reduce la probabilidad de un ataque exitoso mediante un refresh token robado del 23% al 0.3%. Para implementar la detección de reutilización, el servidor almacena el hash del último refresh token emitido junto con el client_id. Al recibir una solicitud de renovación, el servidor compara el refresh token presentado con el almacenado — si no coinciden, se ha producido una reutilización y toda la cadena de tokens se revoca.
Al recibir un error invalid_grant, el cliente debe realizar un cierre de sesión completo: eliminar todos los tokens almacenados (access y refresh), finalizar la sesión actual en el dispositivo y redirigir al usuario a la pantalla de inicio de sesión. La reautenticación crea una nueva cadena de tokens no relacionada con la anterior. Ignorar este error y reintentar la renovación provocará un bloqueo por detección de reutilización.
Un ejemplo de implementación de la renovación de tokens del lado del cliente en Kotlin para Android. La aplicación intercepta la respuesta HTTP 401, desencadena una solicitud de renovación y reintenta la solicitud original con el nuevo access token. Se utiliza OkHttp Interceptor — un componente clave para la gestión automática de tokens sin duplicar la lógica en cada solicitud.
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val accessToken = getAccessToken()
val authRequest = request.newBuilder()
.addHeader("Authorization", "Bearer $accessToken")
.build()
val response = chain.proceed(authRequest)
if (response.code != 401) return response
// El access token ha caducado — renovando mediante refresh token
val newToken = refreshAccessToken() ?: return response
return chain.proceed(request.newBuilder()
.addHeader("Authorization", "Bearer $newToken")
.build())
}
private fun refreshAccessToken(): String? {
val refreshToken = getRefreshToken() ?: return null
val client = OkHttpClient()
val body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("refresh_token", refreshToken)
.build()
val request = Request.Builder()
.url("https://auth.example.com/oauth/token")
.post(body)
.build()
val response = client.newCall(request).execute()
val json = JSONObject(response.body?.string() ?: return null)
val newAccessToken = json.getString("access_token")
// Guardar nuevo refresh token durante rotation
saveTokens(newAccessToken, json.optString("refresh_token"))
return newAccessToken
}
}
Preguntas frecuentes
Access token es un token de corta duración para el acceso a la API, se envía con cada solicitud. Un refresh token es un token de larga duración para obtener un nuevo access token, se envía solo al endpoint de token. El refresh token no debe ser accesible para los endpoints API normales de la aplicación.
En cada caducidad — normalmente cada 15–60 minutos. El cliente debe rastrear el tiempo de caducidad (comprobando exp en JWT o usando un temporizador) e iniciar la solicitud de renovación con antelación, antes de recibir realmente un 401. Esto evita la pérdida de datos en solicitudes enviadas en el momento de la caducidad del token.
Sí, un refresh token puede y debe ser revocado. El servidor mantiene una lista de refresh tokens activos (o sus hashes) en la base de datos. Al cerrar sesión, cambiar la contraseña o detectar actividad sospechosa, el servidor elimina la entrada de la base de datos y la siguiente solicitud de renovación con ese token devolverá un error invalid_grant.
Con rotation y detección de reutilización activadas: la primera solicitud renueva los tokens con éxito, la segunda recibe un error invalid_grant. El servidor también registra la reutilización — la sesión se bloquea, ambos clientes pierden el acceso. El usuario debe iniciar sesión de nuevo. Esto sacrifica la comodidad por la seguridad.
Un refresh token debe almacenarse en Keychain con el atributo kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Esto garantiza el cifrado del token, la inaccesibilidad cuando se elimina el código de acceso y evita la sincronización con iCloud. Está estrictamente prohibido usar UserDefaults o CoreData para almacenar el token.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también