Ir al contenido

Validar JWTs en tu backend

Cuando tu app recibe un id_token del callback OIDC (o un access_token en un Authorization: Bearer), tu backend tiene que validarlo antes de confiar en él. Validar significa cuatro cosas:

  1. Firma correcta contra la public key del issuer (JWKS).
  2. iss = https://auth.<tu_slug>.prysmid.com exacto.
  3. aud = tu client_id.
  4. exp > ahora.

Si te falta alguna de las cuatro, rechazás. No hay “validar más o menos”.

import { createRemoteJWKSet, jwtVerify } from 'jose';
const ISSUER = 'https://auth.acme.prysmid.com';
const AUDIENCE = process.env.PRYSMID_CLIENT_ID;
// JWKS se cachea automáticamente con TTL razonable y refresca al rotar keys.
const jwks = createRemoteJWKSet(new URL(`${ISSUER}/oauth/v2/keys`));
export async function verifyIdToken(token) {
const { payload } = await jwtVerify(token, jwks, {
issuer: ISSUER,
audience: AUDIENCE,
});
return payload; // { sub, email, name, exp, iat, ... }
}

Cachear JWKS, pero no para siempre. Las keys rotan. Si tu librería cachea JWKS por días sin honrar el Cache-Control o el kid mismatch, vas a empezar a rechazar tokens válidos cuando rotemos. Las librerías arriba (jose, PyJWKClient, go-oidc) lo hacen bien por default.

Reloj. Si tu server tiene clock skew de minutos, vas a rechazar tokens recién emitidos por exp. Sincronizá NTP.

access_token vs id_token. El id_token es para tu backend (probás identidad del usuario). El access_token es para hablar con APIs. No los mezcles.

Multi-tenant: validá resourceowner. Si servís a muchos tenants en una sola instancia, no alcanza con iss/aud/exp/sig — también verificá que urn:zitadel:iam:user:resourceowner:id (URN canónica del claim que viene en el JWT) corresponda al tenant que el usuario está intentando acceder. Si no, Bob de Acme podría usar su token de Acme para acceder a recursos de Globex en tu app.

ErrorCausa típica
Invalid signatureCacheaste una JWKS vieja, o tu librería no soporta RS256 (chequeá flags).
Invalid audienceTu client_id cambió, o el token vino de otra app de tu workspace.
Token expiredEl id_token dura ~1h. Refrescá con refresh_token.
Issuer mismatchCambiaste el slug del workspace. El iss lo refleja: auth.<slug>.prysmid.com.