Los clientes pueden reanudar su contexto de soporte sin exponer el signing secret ni un support API token al navegador o al bundle de la app.
Mantén la firma en tu backend
Tu backend autentica al cliente actual, firma un JWT de identidad de cliente DobroDesk de corta duración para la integración elegida y lo devuelve desde un endpoint protegido por la sesión del host. La respuesta del endpoint que usan los wrappers móviles es JSON con un string authorizationToken.
El SDK no crea este JWT y loadDobroDeskToken no es una función del SDK. Es una función proporcionada por el host en los ejemplos siguientes. Nunca incluyas el JWT signing secret, un DobroDesk support API token ni una credencial de servicio de larga duración en JavaScript del navegador, un bundle móvil o un repositorio público.
El widget web también admite sesiones anónimas. Las integraciones nativas necesitan una identidad de cliente firmada antes de las operaciones de red y de presentar el widget. Las peticiones nativas envían la identidad como Authorization: Bearer... con x-dobrodesk-client: native-v1 y no envían un Origin del navegador. Después de iniciar la sesión, el motor nativo conserva la prueba de sesión y la envía como x-dobrodesk-session-token. El host no debe crear ni sustituir esa cabecera.
// This function belongs to your application. The route is your backend route.
async function loadDobroDeskToken(): Promise<string> {
const response = await fetch("/your-backend/dobrodesk-token", {
method: "POST",
credentials: "include",
headers: { Accept: "application/json" },
});
if (!response.ok) throw new Error("Customer token request failed");
const body = (await response.json()) as { authorizationToken?: unknown };
if (typeof body.authorizationToken !== "string" || !body.authorizationToken)
throw new Error("Customer token response is invalid");
return body.authorizationToken;
}
// Web can remain anonymous or apply the token after host login.
widget.setIdentityToken(await loadDobroDeskToken());
// React Native, NativeScript and Capacitor use the signed native shape.
const config = {
apiOrigin: "https://dobrodesk.com",
integrationId: "wgt_0123456789abcdef0123456789abcdef",
accountScope: currentCustomer.id,
credentialProvider: async () => ({
authorizationToken: await loadDobroDeskToken(),
}),
};Configura el signing secret y los claims
En Admin > Channels > Website widgets, abre la integración del widget, establece el identity mode como Require a signed logged-in user y usa Copy identity secret. Guarda el secret específico de la integración junto con el Integration ID público en el gestor de secretos del backend. El secret nunca debe estar en el navegador, el bundle móvil ni un repositorio público. Úsalo como clave de firma HS256.
Firma un JWT HS256 de tres partes con typ JWT. Los claims obligatorios son iss = dobrodesk-widget:{integrationId}, aud = el Integration ID público, sub = un ID estable del cliente que no sea el correo, iat = segundos Unix actuales y exp como máximo 15 minutos después de iat. El verificador permite 60 segundos de diferencia de reloj. Los claims opcionales son name, email, email_verified: true y jti. Establece email_verified solo después de verificar el correo en el host.
const textEncoder = new TextEncoder();
const base64url = (bytes: Uint8Array) => {
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
};
// secretStore is your backend secret manager. Never run this in browser code.
export async function createDobroDeskIdentityToken(user: {
id: string;
email?: string;
emailVerified?: boolean;
name?: string;
}) {
const integrationId = await secretStore.get("DOBRODESK_WIDGET_ID");
const identitySecret = await secretStore.get("DOBRODESK_WIDGET_IDENTITY_SECRET");
const now = Math.floor(Date.now() / 1000);
const encode = (value: unknown) =>
base64url(textEncoder.encode(JSON.stringify(value)));
const header = encode({ alg: "HS256", typ: "JWT" });
const payload = encode({
iss: `dobrodesk-widget:${integrationId}`,
aud: integrationId,
sub: user.id,
iat: now,
exp: now + 5 * 60,
...(user.email
? {
email: user.email,
...(user.emailVerified === true ? { email_verified: true } : {}),
}
: {}),
...(user.name ? { name: user.name } : {}),
});
const key = await crypto.subtle.importKey(
"raw",
textEncoder.encode(identitySecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
const signature = base64url(
new Uint8Array(
await crypto.subtle.sign("HMAC", key, textEncoder.encode(`${header}.${payload}`)),
),
);
return `${header}.${payload}.${signature}`;
}Vincula y borra la identidad de forma deliberada
Usa accountScope como identificador estable del cliente o cuenta del host que no contenga secretos. Aísla el estado de sesión nativo duradero y la outbox. No reutilices un mismo scope para varios clientes autenticados.
En Flutter, React Native, NativeScript o Capacitor, llama a logout antes de terminar la sesión del host o cambiar accountScope. Los clientes iOS y Android directos exponen resetSession en su lugar: reinicia ese cliente y crea uno nuevo para la cuenta siguiente. Llama a resetSession cuando el cliente descarte intencionadamente la conversación actual pero siga autenticado. Renovar un token no es cambiar de cuenta y debe usar el mismo account scope.
Rechaza estados de token inseguros
- Rechaza en el endpoint del host un token vacío, malformado, caducado o de otro cliente en vez de devolver una credencial de soporte.
- Trata un error 401 o session-expired del SDK como una solicitud de un token nuevo para la misma cuenta. No cambies accountScope en silencio ni crees una sesión nueva.
- Si se solicita logout mientras hay una petición de token pendiente, ignora el token tardío y mantén la cuenta siguiente aislada de la sesión anterior.