SSO con JWT
Autentica a tus usuarios en el portal y el widget de Feedjolt con un JWT efímero que firma tu backend. Sin segundo inicio. Configura claims, secretos y rotación.
Si tu aplicacion ya sabe quien es el usuario, no deberias obligarlo a iniciar sesion de nuevo para dejar feedback. Con SSO por JWT tu backend firma un token de corta duracion que identifica al usuario; Feedjolt lo verifica en el servidor y lo inicia de forma transparente en el portal y en el widget integrado.
Como funciona
- Configuras un secreto de firma y un algoritmo en Panel -> Desarrolladores -> SSO con JWT.
- Cuando un usuario autenticado abre el feedback, tu backend genera un JWT con sus datos, firmado con ese secreto.
- Le pasas el token a Feedjolt: en el widget mediante
data-sso-token, o enviandolo por POST al endpoint de identificacion. - Feedjolt verifica la firma, crea o actualiza al usuario final correspondiente y establece una cookie de sesion en el servidor. Sin enlace magico, sin inicio de sesion adicional.
El secreto vive unicamente en tu servidor y en el de Feedjolt. El navegador solo ve el token resultante, nunca el secreto.
Configuracion
El SSO por JWT se configura por espacio de trabajo (solo el propietario) en Panel -> Desarrolladores -> SSO con JWT:
| Campo | Notas |
|---|---|
secret_key | Tu secreto de firma compartido. De 16 a 512 caracteres. Guardalo solo en el servidor. |
algorithm | Uno de HS256 (por defecto), HS384, HS512. Firma HMAC con secreto compartido. |
issuer | Opcional. Si se define, el claim iss del token debe coincidir. |
audience | Opcional. Si se define, el claim aud del token debe coincidir. |
sync_mode | UPSERT (por defecto) crea automaticamente usuarios desconocidos; UPDATE_ONLY rechaza tokens de usuarios que aun no existen. |
Solo se admiten algoritmos HMAC: no hay modo de clave publica (asimetrico). Ambas partes comparten el mismo secreto.
Claims del token
Genera un JWT estandar firmado con tu secreto. Estos claims son obligatorios: un token al que le falte cualquiera de ellos se rechaza.
| Claim | Significado |
|---|---|
sub | Tu ID de usuario estable. Se usa como ID externo del usuario final. |
email | El correo del usuario. |
name | Nombre visible. |
iat | Emitido en (claim estandar de JWT). |
exp | Caducidad (claim estandar de JWT). Mantenla corta: minutos, no dias. |
Los claims opcionales enriquecen el perfil sincronizado:
| Claim | Significado |
|---|---|
avatar_url | URL de la imagen de perfil. |
custom_fields | Objeto de claves/valores arbitrarios que se guarda en el usuario final. |
company | Objeto que identifica la empresa del usuario; vease mas abajo. |
Si configuraste un issuer o un audience, incluye tambien los claims iss / aud correspondientes; solo se verifican cuando estan configurados.
Claim de empresa
Pasa company como objeto para asociar al usuario a un registro de empresa (creado o actualizado al vuelo). Tambien se acepta companies (un array): se usa la primera entrada.
{
"company": {
"id": "org_123",
"name": "Acme Inc",
"plan": "enterprise",
"mrr": 2400,
"industry": "fintech",
"employee_count": 120
}
}Para una empresa solo id y name son obligatorios; el resto es opcional.
Generar el token
Genera el JWT en tu backend, donde el secreto esta protegido. Manten exp corto: el token es de un solo uso para arrancar la sesion.
// Node.js - npm i jsonwebtoken
import jwt from "jsonwebtoken";
const token = jwt.sign(
{
sub: user.id,
email: user.email,
name: user.fullName,
avatar_url: user.avatarUrl,
company: { id: user.orgId, name: user.orgName },
},
process.env.FEEDJOLT_JWT_SECRET,
{ algorithm: "HS256", expiresIn: "5m" },
);# Python - pip install pyjwt
import datetime, jwt
now = datetime.datetime.now(datetime.timezone.utc)
token = jwt.encode(
{
"sub": user.id,
"email": user.email,
"name": user.full_name,
"avatar_url": user.avatar_url,
"company": {"id": user.org_id, "name": user.org_name},
"iat": now,
"exp": now + datetime.timedelta(minutes=5),
},
FEEDJOLT_JWT_SECRET,
algorithm="HS256",
)Entregar el token a Feedjolt
Widget: pasa el token en el atributo data-sso-token del loader (consulta Configuracion del widget). El widget lo reenvia y el usuario queda autenticado dentro del panel.
Portal / directo: envia el token por POST al endpoint de identificacion de tu espacio de trabajo:
curl -X POST https://api.feedjolt.com/api/{workspace}/identify \
-H "Content-Type: application/json" \
-d '{"token": "TU_JWT_AQUI"}'Si todo va bien, Feedjolt devuelve el usuario final sincronizado y establece una cookie de sesion en el servidor (httpOnly, secure, SameSite=Lax). Esa cookie, y no tu JWT, es la que mantiene la sesion despues, asi que solo generas un JWT nuevo de corta duracion al iniciar sesion.
Rotacion de claves
Rotar el secreto de firma no genera tiempo de inactividad. Cuando guardas un secreto nuevo, Feedjolt mantiene valido el anterior para que los tokens ya en circulacion sigan verificandose. Durante la transicion se aceptan hasta los ultimos 5 secretos; los mas antiguos se descartan automaticamente.
Para rotar: define el nuevo secreto en el panel, despliegalo en tu backend y deja que se agoten los tokens en circulacion. No hace falta un cambio coordinado.
Notas de seguridad
- La verificacion es del lado del servidor. Feedjolt valida la firma, la caducidad y (si estan definidos) el issuer/audience en sus propios servidores. Nunca confies en una identidad declarada por el navegador.
- El secreto nunca llega al navegador. Genera los tokens solo en tu backend. Trata el secreto como una contrasena.
- Manten
expcorto. El JWT solo necesita durar lo justo para arrancar la cookie de sesion: con minutos basta. UPDATE_ONLYpara sistemas cerrados. Si aprovisionas usuarios por otro medio y no quieres que el SSO cree nuevos, definesync_modecomoUPDATE_ONLY.
Resolución de problemas MCP
Soluciona errores comunes del MCP de Feedjolt: 402 plan requerido, 401 no autorizado, 403 scope insuficiente, 429 límite de tasa, etiqueta no encontrada, y los guardarraíles al eliminar tableros y estados.
Sandbox y prueba
Cómo agentes y desarrolladores prueban Feedjolt sin una llamada de ventas: alta autoservicio, prueba Growth y claves API en Startup.
