Definicion
Piensa en la diferencia entre tu carnet de conducir y la tarjeta de socio del gimnasio. Tu carnet de conducir prueba quien eres - tu nombre, foto, fecha de nacimiento, direccion. Es un documento de identidad. Tu tarjeta de socio del gimnasio prueba lo que puedes hacer - acceder al gimnasio, usar el equipamiento, asistir a clases. Es un documento de autorizacion. En el mundo de la autenticacion, un ID Token es como tu carnet de conducir - contiene claims verificados sobre tu identidad. Es completamente separado del Access Token (mas parecido a la tarjeta del gimnasio) que otorga permisos para hacer cosas.
Los ID Tokens fueron introducidos por OpenID Connect (OIDC), que anade funcionalidad de identidad sobre OAuth 2.0. Cuando haces clic en “Iniciar sesion con Google” en un sitio web, el sitio recibe tanto un access token (para llamar APIs de Google si es necesario) COMO un ID token (conteniendo tu identidad verificada). El ID token es un JWT (JSON Web Token) con claims especificos: tu identificador unico (sub), email, nombre, URL de foto de perfil, y cuando/como te autenticaste. El sitio web usa esto para establecer tu sesion - saben exactamente quien eres porque Google lo ha verificado.
La distincion crucial: los ID tokens estan pensados para ser consumidos por tu aplicacion para identificar al usuario, NO enviados a APIs para autorizacion. Cuando tu app recibe un ID token de Google, verifica la firma, comprueba que Google es el emisor y que tu app es la audiencia prevista, y extrae la informacion del usuario para crear una sesion. Pero al llamar APIs, usas el access token en su lugar. Esta separacion importa por seguridad - los ID tokens contienen informacion personal que no deberia difundirse a cada API, mientras que los access tokens estan disenados para ese proposito.
Ejemplo
Escenario Real 1: Flujo “Iniciar sesion con Google” Haces clic en “Continuar con Google” en Canva.com. Google te autentica (contrasena, 2FA, lo que sea). Google te redirige de vuelta a Canva con ambos tokens. Canva ignora el access token (no necesitan llamar APIs de Google). Validan la firma del ID token contra el JWKS de Google, verifican que el claim de audiencia coincide con su client ID, extraen tu email y nombre, y crean una sesion de Canva. El ID token le dijo a Canva exactamente quien eres, verificado por Google.
Escenario Real 2: Single Sign-On Empresarial Tu empresa usa Okta para SSO. Cuando inicias sesion en el portal interno de RRHH, el portal te redirige a Okta, te autenticas, y Okta devuelve un ID token. El portal de RRHH extrae tu ID de empleado, departamento y roles de los claims del ID token. Crea tu sesion con los permisos especificos del departamento. El ID token llevo tu identidad corporativa verificada a traves del limite de autenticacion.
Escenario Real 3: Identificacion de Usuario en App Movil Una app de fitness permite a usuarios iniciar sesion con Apple. Despues de Apple Sign In, la app recibe un ID token conteniendo el Apple ID del usuario (identificador anonimo), email (si se compartio) y nombre. La app crea o busca la cuenta de usuario basandose en el claim “sub” (Apple ID unico), personaliza la experiencia con el nombre del usuario, e inicia la sesion de seguimiento de fitness. El ID token establecio quien es el usuario.
Escenario Real 4: Cuenta de Gaming Multi-Plataforma Una plataforma de gaming soporta multiples proveedores de identidad. Cuando inicias sesion via Discord, Twitter o Google, cada uno proporciona un ID token con la identidad del usuario. La plataforma extrae el identificador unico y proveedor de cada ID token, los vincula a una sola cuenta de gaming, y sabe que [email protected] (Google), @JohnGamer (Discord), y @john_plays (Twitter) son todos la misma persona. ID tokens de diferentes proveedores se unifican en una identidad.
Analogia
El Documento de Identidad Gubernamental: Tu pasaporte o carnet de conducir prueba quien eres - nombre, foto, fecha de nacimiento, nacionalidad. Es emitido por una autoridad de confianza (gobierno) y puede verificarse como autentico. Lo muestras para probar identidad, no para ganar acceso a servicios especificos. Los ID tokens funcionan identicamente - son documentos de identidad gubernamentales para el mundo digital, emitidos por proveedores de identidad de confianza (Google, Okta, Auth0).
La Credencial de Nombre en una Conferencia: Cuando te registras en una conferencia, recibes una credencial con tu nombre verificado, empresa y tipo de asistente. Esta credencial no te deja entrar a sesiones especificas (eso es tu ticket/access token), pero le dice a todos quien eres. La llevas en el pecho (el ID token viaja contigo) para que la gente conozca tu identidad. El ID token es tu credencial de conferencia verificada.
La Pulsera de Identificacion Medica: Una pulsera de identificacion hospitalaria muestra tu nombre, fecha de nacimiento y numero de historial medico - informacion de identidad verificada. No te da acceso a medicacion (eso lo hace la prescripcion del doctor), pero asegura que el personal sepa exactamente quien eres antes de cualquier tratamiento. El ID token es esta pulsera medica - identidad verificada que precede cualquier decision de autorizacion.
La Tarjeta de Estudiante con Foto: Tu carnet de estudiante muestra tu foto, nombre, numero de estudiante y ano. Prueba que eres estudiante de esa escuela (identidad). Diferentes derechos de acceso (biblioteca fuera de horario, acceso al laboratorio, plan de comidas) estan codificados separadamente o gestionados por otros sistemas. El carnet de estudiante es tu identidad verificada; los permisos de acceso estan separados. Los ID tokens funcionan igual.
Ejemplo de Codigo
// Decodificar y validar ID token
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: 'https://oauth.provider.com/.well-known/jwks.json',
cache: true,
cacheMaxAge: 600000
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
callback(null, key.getPublicKey());
});
}
// Verificar ID token con todas las comprobaciones requeridas
async function verifyIdToken(idToken, expectedClientId, expectedIssuer) {
return new Promise((resolve, reject) => {
jwt.verify(idToken, getKey, {
audience: expectedClientId, // Debe coincidir con tu client ID
issuer: expectedIssuer, // Debe coincidir con el proveedor esperado
algorithms: ['RS256'], // Debe coincidir con el algoritmo esperado
clockTolerance: 30 // Permitir 30 segundos de desfase de reloj
}, (err, decoded) => {
if (err) {
return reject(new Error(`ID token invalido: ${err.message}`));
}
// Validaciones adicionales
if (!decoded.sub) {
return reject(new Error('ID token sin claim subject'));
}
// Comprobar nonce si enviaste uno (previene ataques de replay)
// if (decoded.nonce !== expectedNonce) { ... }
resolve(decoded);
});
});
}
// Uso: Despues del callback OAuth
app.get('/callback', async (req, res) => {
const { code } = req.query;
// Intercambiar codigo por tokens
const tokens = await exchangeCodeForTokens(code);
// Verificar y extraer identidad del ID token
const identity = await verifyIdToken(
tokens.id_token,
process.env.CLIENT_ID,
'https://accounts.google.com'
);
// Usar identidad para crear sesion
const user = await findOrCreateUser({
providerId: identity.sub,
email: identity.email,
name: identity.name,
picture: identity.picture
});
req.session.userId = user.id;
res.redirect('/dashboard');
});
// Ejemplo de claims de ID token (decodificado)
{
"iss": "https://accounts.google.com", // Emisor
"sub": "110169484474386276334", // ID unico de usuario
"aud": "your-client-id.apps.google", // Tu aplicacion
"exp": 1700000000, // Expiracion
"iat": 1699996400, // Emitido en
"email": "[email protected]",
"email_verified": true,
"name": "John Doe",
"picture": "https://lh3.google.../photo.jpg",
"locale": "es"
}
Diagrama
flowchart TB
subgraph IDToken["ID Token (JWT)"]
direction TB
Header["Header
alg: RS256
typ: JWT
kid: key-123"]
Payload["Payload (Claims)
iss: https://accounts.google.com
sub: 110169484474386276334
aud: tu-client-id
exp: 1700000000
iat: 1699996400
email: [email protected]
name: Juan Garcia"]
Signature["Firma
RSASHA256(header + payload)"]
end
Header --> Payload --> Signature
subgraph Proposito["Proposito: IDENTIDAD"]
Identidad["Quien es este usuario?"]
NoAuth["NO para autorizacion de API"]
end
IDToken --> Proposito
Note1["ID Token responde: QUIEN eres?"]
Note2["Access Token responde: QUE puedes hacer?"]