Iniciar sesión con UTILIA con el SDK JavaScript
Implementa «Iniciar sesión con UTILIA» con @utilia-os/sdk-js: URL de autorización con PKCE, canje del código, datos de la persona y cierre de sesión.
Actualizado el . 3 min de lectura
El SDK JavaScript de UTILIA OS (@utilia-os/sdk-js) resuelve el flujo OAuth por ti: genera el par PKCE y el state, construye la URL de autorización, canjea el código, guarda los tokens y los renueva antes de que caduquen. Esta guía muestra el inicio de sesión en una aplicación web y en Node.js.
Antes de empezar
- Activa OAuth en tu aplicación externa, como explica Activar OAuth en tu aplicación. Para una aplicación web sin servidor, elige Sin servidor (web o móvil); para Node.js con servidor, Con servidor propio.
- Registra como dirección de vuelta la ruta de tu aplicación que recibirá el código, por ejemplo
http://localhost:3000/callback. - Instala el SDK:
npm install @utilia-os/sdk-jsAplicación web
En el navegador hay una trampa: entre la ida a UTILIA OS y la vuelta, la página se recarga entera, y el almacenamiento por defecto del SDK vive en memoria. Para que el code_verifier y el state sobrevivan, dale un almacenamiento que use sessionStorage:
Lo que tiene que sobrevivir a la recarga
Tu página
getAuthorizationUrl()
Genera el code_verifier y el state y los guarda en tu almacenamiento.Navegador
Ida y vuelta a UTILIA OS
La página se descarga: lo que estaba en memoria se pierde.Tu página de vuelta
handleCallback(code, undefined, state)
Lee lo guardado, comprueba el state y canjea el código.Si no los encuentra: «No se encontró el estado PKCE pendiente».
Crea el almacenamiento y el SDK
TypeScriptimport { UtiliaSDK } from "@utilia-os/sdk-js"; import type { OAuthTokens, PendingOAuthState, TokenStorage } from "@utilia-os/sdk-js"; class SessionTokenStorage implements TokenStorage { private read<T>(key: string): T | null { const raw = sessionStorage.getItem(key); return raw ? (JSON.parse(raw) as T) : null; } async getTokens() { return this.read<OAuthTokens>("utilia.tokens"); } async setTokens(tokens: OAuthTokens) { sessionStorage.setItem("utilia.tokens", JSON.stringify(tokens)); } async clearTokens() { sessionStorage.removeItem("utilia.tokens"); } async getPendingState() { return this.read<PendingOAuthState>("utilia.pending"); } async setPendingState(state: PendingOAuthState) { sessionStorage.setItem("utilia.pending", JSON.stringify(state)); } async clearPendingState() { sessionStorage.removeItem("utilia.pending"); } } export const sdk = new UtiliaSDK({ baseURL: "https://os.utilia.ai/api", oauth: { clientId: "utilia_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", redirectUri: "http://localhost:3000/callback", scopes: ["openid", "profile", "email"], tokenStorage: new SessionTokenStorage(), }, });En una aplicación de navegador no pongas
clientSecret: no se puede guardar en secreto. PKCE la protege.Lleva a la persona a autorizar
TypeScriptconst authUrl = await sdk.oauth.getAuthorizationUrl(); window.location.href = authUrl;getAuthorizationUrl()genera el par PKCE y elstate, los guarda en tu almacenamiento y devuelve la URL. Admite opciones comoscopes,resource,prompt,loginHintynonce.Canjea el código en la página de vuelta
TypeScriptconst params = new URLSearchParams(window.location.search); const code = params.get("code"); const state = params.get("state"); if (code) { // Con el state, el SDK comprueba que la respuesta es la de tu petición. const tokens = await sdk.oauth.handleCallback(code, undefined, state ?? undefined); console.log(tokens.scope); // lo que la persona concedió de verdad }Si
params.get("error")valeaccess_denied, la persona no ha autorizado.Obtén los datos de la persona
TypeScriptconst user = await sdk.oauth.getUserInfo(); console.log(user.sub); // identificador estable console.log(user.name); console.log(user.email);getUserInfo()llama aGET /api/oauth/userinfocon el access token y lo renueva antes si está a punto de caducar.
Node.js o línea de órdenes
En un proceso de Node.js, FileTokenStorage guarda los tokens y el estado pendiente en un archivo, así que sobreviven entre ejecuciones:
import { UtiliaSDK, FileTokenStorage } from "@utilia-os/sdk-js";
const sdk = new UtiliaSDK({
baseURL: "https://os.utilia.ai/api",
oauth: {
clientId: process.env.UTILIA_CLIENT_ID!,
clientSecret: process.env.UTILIA_CLIENT_SECRET, // solo con servidor propio
redirectUri: "http://localhost:8080/callback",
scopes: ["openid", "profile", "email"],
tokenStorage: new FileTokenStorage(".utilia-tokens.json"),
},
});
if (await sdk.oauth.isAuthenticated()) {
const user = await sdk.oauth.getUserInfo();
console.log(`Hola, ${user.name}`);
} else {
console.log("Abre esta URL:", await sdk.oauth.getAuthorizationUrl());
// Después, con el code y el state de la redirección:
// await sdk.oauth.handleCallback(code, undefined, state);
}isAuthenticated() es asíncrono: sin await la condición siempre sería verdadera.
Renovación y errores
El SDK renueva el access token automáticamente unos segundos antes de que caduque y guarda el refresh token nuevo que recibe en cada renovación, como exige la rotación del refresh token. Si varias peticiones necesitan renovar a la vez, comparten una sola renovación.
Si la renovación ya no es posible (la cadena caducó o se revocó, o la persona perdió el acceso), la llamada falla. Trátalo volviendo a pedir la autorización:
try {
const user = await sdk.oauth.getUserInfo();
} catch {
await sdk.oauth.revokeToken(); // limpia lo guardado
window.location.href = await sdk.oauth.getAuthorizationUrl();
}Cerrar sesión
await sdk.oauth.logout();Revoca el access token en UTILIA OS, lo que anula también el refresh token de esa emisión, y borra los tokens guardados.
Qué puedes hacer con el token
El token de este flujo sirve para identificar a la persona y para llamar a los servidores MCP de UTILIA OS en su nombre (pide su resource y sus scopes al autorizar). Los servicios REST del SDK, como sdk.tickets o sdk.users, usan la API externa y se configuran con la clave de API de tu aplicación (apiKey), no con OAuth.
La referencia completa de la configuración OAuth del SDK está en SDK JavaScript: OAuth. Para el estilo del botón, Botones y marca.