---
term: 'JSON Web Token (JWT)'
seoTitle: '¿Qué es un JWT? Estructura, claims, seguridad y revocación'
headline: '¿Qué es un JSON Web Token (JWT)?'
slug: json-web-token-jwt
category: auth-security
shortDefinition: 'Un JSON Web Token es un token compacto y seguro para URLs que transporta claims JSON firmadas, y permite verificar solicitudes sin sesiones almacenadas.'
relatedTerms:
  - oauth-2-social-login
  - api-key-security
  - identity-access-management-iam
  - access-control-lists-acl
contrastsWith:
  - api-key-security
aboutTerms:
  - 'Claims de JWT'
  - 'JWS (JWT firmado)'
  - 'Refresh Tokens'
faq:
  - question: '¿Qué es un JWT en términos simples?'
    answer: 'Una "cédula de identidad" JSON firmada y codificada en Base64 que un servidor emite al iniciar sesión. El cliente la presenta en cada solicitud y el servidor verifica la firma en lugar de buscar una sesión — el token mismo lleva quién eres y hasta cuándo. Oficialmente se pronuncia como "jot" en inglés, según el RFC que lo define.'
  - question: '¿Cuáles son las tres partes de un JWT?'
    answer: 'Header (tipo de token y algoritmo de firma), payload (las claims — los datos JSON reales) y firma, cada una codificada en Base64Url y unidas por puntos en header.payload.signature. La firma se calcula sobre las dos primeras partes, así que cualquier manipulación de ellas rompe la verificación.'
  - question: '¿Un JWT está cifrado?'
    answer: 'No — está codificado, no cifrado. Base64Url es un formato de transporte que cualquiera puede revertir; pega cualquier JWT en un decodificador y el payload se lee completo. La firma impide manipular, no leer. Nunca pongas secretos ni datos personales sensibles en el payload de un JWT; la variante cifrada (JWE) existe para necesidades genuinas de confidencialidad.'
  - question: '¿Cuál es la diferencia entre un JWT y un token de sesión?'
    answer: 'Dónde vive el estado. Un token de sesión es un ID opaco que apunta a estado del lado del servidor — una consulta por solicitud, revocable al instante. Un JWT lleva el estado dentro de sí — sin consulta, verificable por cualquier servicio que tenga la clave, pero válido hasta expirar pase lo que pase. Escalado stateless frente a control instantáneo.'
  - question: '¿Dónde debería guardar un JWT en el navegador?'
    answer: 'No en localStorage — cualquier script inyectado puede leerlo, y un XSS se convierte en robo de tokens. El consenso actual: mantén el access token en memoria, el refresh token en una cookie HttpOnly, Secure y SameSite, y considera el patrón backend-for-frontend, que mantiene los tokens fuera del navegador por completo.'
  - question: '¿Se puede revocar un JWT?'
    answer: 'No por diseño — un token firmado es válido hasta su claim exp, y ese es el precio de no tener estado. Todas las soluciones reintroducen estado: una denylist basada en la claim jti, claves versionadas o — la respuesta estándar — access tokens de vida muy corta emparejados con refresh tokens revocables y con rotación.'
  - question: '¿Cuál es la diferencia entre HS256 y RS256?'
    answer: 'Simetría. HS256 firma y verifica con un único secreto compartido — adecuado solo cuando emisor y verificador son la misma parte, y solo con un secreto largo y aleatorio. RS256 (y el más nuevo EdDSA) firma con una clave privada mientras cualquiera verifica con la pública — el default cuando más de un servicio comprueba tokens.'
  - question: '¿Cuál es la diferencia entre JWT y OAuth?'
    answer: 'Categorías distintas: JWT es un formato de token; OAuth 2.0 es un marco de autorización. Se componen — los flujos de OAuth suelen emitir access tokens que resultan ser JWTs, y los ID tokens de OpenID Connect siempre lo son. Uno dice cómo se construye un token; el otro, cómo se reparten los tokens.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'RFC 7519 — JSON Web Token (JWT)'
    url: 'https://datatracker.ietf.org/doc/html/rfc7519'
  - name: 'RFC 8725 — JWT Best Current Practices'
    url: 'https://datatracker.ietf.org/doc/html/rfc8725'
  - name: 'OWASP JSON Web Token Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html'
  - name: 'RFC 7515 — JSON Web Signature (JWS)'
    url: 'https://datatracker.ietf.org/doc/html/rfc7515'
  - name: 'JSON Web Token — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/JSON_Web_Token'
cta:
  title: 'Auth que ya tomó estas decisiones'
  text: 'Back4app maneja las sesiones con tokens revocables, verifica del lado del servidor los JWTs de los proveedores de identidad para el login social y aplica permisos por usuario en cada solicitud — higiene de tokens como comportamiento de plataforma.'
  linkText: 'Empieza gratis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-08-28'
translationKey: json-web-token-jwt
---

**Un JSON Web Token es un token compacto y seguro para URLs que transporta claims JSON firmadas, y permite verificar solicitudes sin sesiones almacenadas.** Ese es el encuadre del propio [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) — "un medio compacto y seguro para URLs de representar claims que se transfieren entre dos partes" — y las dos ideas que contiene cargan con todo lo que sigue: el token *contiene* sus hechos, y una firma hace que esos hechos sean *comprobables* por cualquiera que tenga la clave correcta, sin base de datos de por medio.

## Puntos clave

| Pregunta | Respuesta |
| --- | --- |
| La forma | `header.payload.signature` — tres partes Base64Url unidas por puntos |
| El truco | Cualquiera puede *decodificarlo*; solo quien tiene la clave puede *forjarlo* |
| No es cifrado | El payload es legible — firmado ≠ secreto (ese es el trabajo de JWE) |
| El intercambio | Verificación stateless ↔ sin revocación integrada hasta `exp` |
| La disciplina | Fija los algoritmos · valida `iss`/`aud`/`exp` · vidas cortas + rotación de refresh |

## Un JWT, decodificado

```text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJzdWIiOiJ1c3ItOGZrMiIsInJvbGUi… . dBjftJeZ4CVP…

header     { "alg": "HS256", "typ": "JWT" }
payload    { "sub": "usr-8fk2",  "role": "editor",
             "iss": "https://api.example.com",  "aud": "example-web",
             "iat": 1767024900,  "exp": 1767025800 }        ← vida de 15 minutos
signature  HMACSHA256( base64url(header) + "." + base64url(payload), secret )

Cualquiera puede DECODIFICAR las dos primeras partes — Base64Url es empaquetado, no cifrado.
Solo quien tiene la clave puede FORJAR la tercera — y ese es todo el truco.
```

El contraste que vale la pena ver en código — la alternativa *con estado* contra la que se mide cada decisión de JWT:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// The stateful contrast: Back4app issues revocable session tokens
const user = await Parse.User.logIn('ada', 'correct-horse-battery');
const token = user.getSessionToken(); // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await Parse.User.logOut(); // token invalid NOW — no waiting for an exp claim
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The stateful contrast: Back4app issues revocable session tokens
final user = ParseUser('ada', 'correct-horse-battery', null);
await user.login();
final token = user.sessionToken; // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await user.logout(); // token invalid NOW — no waiting for an exp claim
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The stateful contrast: Back4app issues revocable session tokens
let user = try await User.login(username: "ada", password: "correct-horse-battery")
let token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
try await User.logout() // token invalid NOW — no waiting for an exp claim
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The stateful contrast: Back4app issues revocable session tokens
val user = ParseUser.logIn("ada", "correct-horse-battery")
val token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
ParseUser.logOut() // token invalid NOW — no waiting for an exp claim
```

## Cómo funciona la verificación

```mermaid
flowchart LR
  accTitle: Flujo de emisión y verificación de un JWT
  accDescr: En el login el servidor firma un token que contiene claims y lo devuelve al cliente. El cliente lo guarda y lo envía como bearer token en cada solicitud. El servidor verifica la firma con su clave y valida las claims, aceptando o rechazando sin ninguna consulta de sesión.
  L["Login<br/>(credenciales verificadas una vez)"] --> S["El servidor firma el JWT<br/>claims + clave"]
  S --> C["El cliente guarda el token"]
  C -->|"Authorization: Bearer eyJ…"| V["Cualquier servidor con la clave:<br/>verifica la firma · valida las claims"]
  V -->|"válido"| OK["La solicitud procede<br/>sin consulta de sesión"]
  V -->|"manipulado / expirado / aud equivocada"| NO["401"]
```

Una precisión que los explicadores se saltan: "JWT" nombra el formato de claims; lo que todo el mundo pasa de mano en mano es en realidad un **JWS** ([RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515)) — la serialización *firmada* — mientras que **JWE** es el hermano cifrado para payloads que deben permanecer ilegibles. Y la verificación son dos trabajos, no uno: comprobar la firma y luego **validar las claims** — `exp` y `nbf` contra el reloj, `iss` contra tu allowlist de emisores, `aud` contra el identificador de *este servicio*. Un token perfectamente firmado que está expirado, viene del emisor equivocado o fue acuñado para otra audiencia es un ataque perfectamente firmado.

## Claims: el vocabulario del payload

| Claim | Nombre | Trabajo del verificador |
| --- | --- | --- |
| `iss` | Emisor (issuer) | ¿Es un emisor en el que confío? |
| `sub` | Sujeto (subject) | ¿De quién trata? — el ID estable del usuario |
| `aud` | Audiencia (audience) | ¿Se acuñó para *mí*? Rechaza los tokens ajenos |
| `exp` | Expiración | Rechaza después de este timestamp Unix |
| `iat` / `nbf` | Emitido en / no antes de | Comprueba la ventana de validez |
| `jti` | ID del token | Identificador único — el gancho que necesita una denylist |
| *custom* | Roles, tenant, plan… | Semántica de la app — mínimas, nunca secretas |

Mantén los payloads magros por partida doble: los tokens viajan en cada solicitud como headers (las cookies topan cerca de los 4 KB, y cada claim es ancho de banda repetido), y todo lo que llevan es legible para quien tenga el token.

## JWT vs. tokens de sesión

| | JWT (stateless) | Token de sesión (con estado) |
| --- | --- | --- |
| El token es | El estado mismo, firmado | Un puntero opaco a estado del servidor |
| Costo por solicitud | Verificación de firma, sin consulta | Una consulta al almacén de sesiones |
| Revocación | **Ninguna hasta `exp`** — por diseño | Instantánea — borra la sesión |
| Auth entre servicios | Cualquier servicio con la clave verifica | Los servicios deben compartir el almacén de sesiones |
| Logout significa | El cliente lo descarta; el token sigue válido | El token muere de verdad |
| Mejor hogar | APIs, microservicios, verificación por terceros | Apps de un solo backend, sesiones sensibles |

La verdad pasada de moda con la que ahora abren varias guías de buenas prácticas: para una app renderizada en el servidor con un solo backend, las sesiones del framework son más simples *y* más controlables — los JWTs se ganan su lugar cuando los tokens deben verificarse entre servicios o por partes que no deberían llamar a casa en cada solicitud.

## El problema de la revocación, con honestidad

La ausencia de estado (statelessness) *es* la incapacidad de revocar — la misma propiedad, descrita dos veces. Un token firmado es válido hasta `exp` sin importar lo que haya pasado desde entonces: logout, cambio de contraseña, baneo de la cuenta. Toda solución reintroduce estado, así que elige el sabor: una **denylist** basada en `jti` + `iss` (la construcción [recomendada por OWASP](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html)) consultada por solicitud — poco estado, pero estado; el **versionado de claves**, que revoca a *todos* a la vez; o la arquitectura estándar — **access tokens de vida corta (5–15 minutos) más refresh tokens revocables** con rotación: cada refresh emite un refresh token nuevo y retira el anterior, así uno robado muere en el primer replay, y el reúso de un token retirado delata el robo y mata a toda la familia de tokens. La latencia de revocación pasa a ser igual a la vida del access token — que es la verdadera razón de que esas vidas sean cortas.

## Elegir un algoritmo — y los ataques a esa elección

| | HS256 (HMAC) | RS256 (RSA) | EdDSA / ES256 |
| --- | --- | --- | --- |
| Claves | Un secreto compartido | La privada firma, la pública verifica | La privada firma, la pública verifica |
| Los verificadores necesitan | ¡El *secreto* (que también puede forjar)! | Solo la clave pública | Solo la clave pública |
| Encaja en | Emisor = verificador, una sola parte | Multi-servicio, terceros | Lo mismo, firmas más pequeñas/rápidas |
| Filo peligroso | Los secretos cortos se rompen por fuerza bruta offline desde un solo token capturado — usa ≥256 bits aleatorios | Tokens más grandes, más lento | Soporte de bibliotecas más nuevo |

La sección de ataques por la que existe el [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725), en tres frases. `alg: "none"` es un valor legal del header — las bibliotecas que lo honran aceptan tokens sin firma. El ataque de confusión: a un verificador que deja que el *header del token* elija el algoritmo se le puede entregar un token "HS256" firmado con la clave RSA *pública* del servidor usada como secreto HMAC — un valor público usado como privado. Ambos mueren de la misma manera: **el verificador fija sus algoritmos y claves aceptados en la configuración y jamás confía en el header para elegir.**

## Dónde guardar los JWTs en el navegador

| Ubicación | ¿XSS lo roba? | ¿CSRF lo envía? | ¿Sobrevive al refresh? | Veredicto |
| --- | --- | --- | --- | --- |
| localStorage | **Sí** | No | Sí | Evítalo — legible por scripts |
| Cookie simple | Sí (legible por scripts) | **Sí** | Sí | Lo peor de ambos |
| Cookie HttpOnly + Secure + SameSite | No | Mitigado por SameSite | Sí | Bien — para el refresh token |
| En memoria | Solo mientras hay inyección | No | No | Bien — para el access token |
| El backend-for-frontend guarda los tokens | El navegador nunca los tiene | Aplican las reglas de cookies | Sí | Lo más fuerte para SPAs |

El modelo de amenazas, no el folclore: localStorage cambia inmunidad a CSRF por robo vía XSS, las cookies cambian lo inverso — por eso el consenso parte la pareja: access token en memoria, refresh token en una cookie endurecida.

## Casos de uso comunes

- **Autenticación de APIs** — el bearer token detrás de los headers `Authorization` en APIs [REST](/glossary/es/api-rest/) y GraphQL.
- **Identidad en microservicios** — un token emitido por el gateway y verificado de forma independiente por cada servicio, sin almacén de sesiones compartido.
- **ID tokens de OpenID Connect** — la afirmación de identidad firmada en cada [login social](/glossary/oauth-2-social-login/) — siempre un JWT.
- **Traspasos entre sistemas** — enlaces firmados, payloads de webhooks, permisos de descarga: claims verificadas por una parte que no puede devolverte la llamada.
- **Pistas de autorización stateless** — roles y IDs de tenant transportados en claims, con la verificación autoritativa todavía del lado del servidor.

## ¿Deberías usar JWTs o sesiones? Matriz de decisión

| Tu situación | Elige |
| --- | --- |
| Un solo backend, app renderizada en el servidor | Sesiones — más simples, revocables al instante |
| API pública consumida por muchos servicios | JWTs, claves asimétricas |
| Microservicios detrás de un gateway | JWTs — verificación local, sin almacén compartido |
| El bloqueo instantáneo es un requisito duro | Sesiones, o JWTs + denylist y `exp` corto |
| Terceros deben verificar tus afirmaciones | JWTs — ese es el terreno natal del formato |
| App móvil contra un BaaS | El mecanismo de sesión de la plataforma — ya eligió por ti |

## Limitaciones y trade-offs

- **La irrevocabilidad es estructural.** Cada mitigación — denylists, vidas cortas, rotación — es una devolución parcial del estado que quitaste; ponle precio antes de elegir stateless.
- **Las claims se vuelven obsoletas.** Los roles son una foto del momento de emisión; un admin degradado sigue siendo admin hasta la expiración. Las vidas cortas acotan la ventana de obsolescencia.
- **El payload es público.** Trátalo como legible por el usuario y por cualquier ladrón de tokens — identificadores sí, secretos y PII no.
- **Los defaults de las bibliotecas han quemado gente.** Fijar algoritmos, validar claims y comprobar `typ` son responsabilidades de tu configuración, no garantías de la biblioteca.
- **El tamaño se acumula.** Cada claim viaja en cada solicitud; los payloads generosos gravan el ancho de banda móvil y pueden desbordar los límites de las cookies.

## Tokens en Back4app

Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. La elección de la propia plataforma ilustra la matriz de decisión: las sesiones de cliente usan **tokens de sesión revocables del lado del servidor** — las pestañas de código de arriba — de modo que el logout, los cambios de contraseña y la terminación de sesiones desde el dashboard surten efecto de inmediato, sin ventana de expiración que esperar. Los JWTs aparecen donde corresponden: los ID tokens OIDC de los proveedores de identidad se verifican del lado del servidor mediante adaptadores de auth durante el [login social](/glossary/oauth-2-social-login/), y las funciones de Cloud Code pueden acuñar o verificar JWTs para traspasos con terceros usando bibliotecas estándar — con el secreto de firma en la configuración del servidor, nunca enviado a los clientes. Con estado donde importa el control, stateless en los bordes donde la verificación debe viajar: la arquitectura por la que aboga este artículo, preensamblada.
