SDK de JavaScript para navegador
@keenauth/js es el núcleo para navegador de KeenAuth. Permite que los usuarios inicien sesión en su aplicación de una sola página, conserva sus tokens y realiza llamadas a su API. No tiene dependencias de tiempo de ejecución, y los componentes de React se basan en él. El paquete todavía no está publicado en npm.
Iniciar sesión con redirección
El SDK es un cliente público estándar de OpenID Connect:
- Utiliza PKCE (S256) y tokens vinculados a DPoP.
- Comprueba
state,nonceeissen la devolución de llamada. - Valida la firma y los claims del ID token con las claves de su entorno.
import { KeenAuth } from "@keenauth/js";
const auth = new KeenAuth({
issuer: "https://acme.eu.keenauth.app", // your environment's issuer, exactly
clientId: "app_01j9zzzzzzzzzzzzzzzzzzzz",
redirectUri: "https://app.acme.example/callback",
postLogoutRedirectUri: "https://app.acme.example/",
});
// On your sign-in button:
await auth.signInRedirect({ appState: "/orders" });
// On https://app.acme.example/callback:
const { appState } = await auth.handleRedirectCallback();
handleRedirectCallback acepta una respuesta solo una vez. Falla con un KeenAuthError cuando:
stateoissno coinciden;- el inicio de sesión pendiente tiene más de 10 minutos;
- el ID token no es válido.
Un error que envía el servidor se lanza como KeenAuthOAuthError con el código error de OAuth.
Llamar a su API
Para obtener un access token para su API, pase su identificador como resource (RFC 8707). Se envía en la solicitud de autorización, en el intercambio del código y en cada renovación, y el aud del token es ese identificador:
const auth = new KeenAuth({
issuer: "https://acme.eu.keenauth.app",
clientId: "app_01j9zzzzzzzzzzzzzzzzzzzz",
redirectUri: "https://app.acme.example/callback",
resource: "https://api.acme.example", // your API's identifier
scope: "openid profile email offline_access messages:read",
});
- El valor debe ser un URI absoluto sin fragmento (
#). Se envía exactamente como lo escriba, así que use el identificador de la API tal como está registrado. Un valor no válido lanza unKeenAuthErrorcon el códigoconfig_invaliddesde el constructor. signInRedirect({ resource })lo sustituye para un único inicio de sesión.- Sin
resource, el servidor usa la API predeterminada de la aplicación, si hay una configurada; de lo contrario, no se conceden permisos de API. - La opción también acepta un array, que se envía como parámetros
resourcerepetidos, como permite RFC 8707. Un access token de KeenAuth tiene exactamente una audiencia, así que el servidor rechaza más de un valor coninvalid_target(unKeenAuthOAuthError).
const res = await auth.authorizedFetch("https://api.acme.example/v1/orders");
// Or take the token yourself; it is refreshed when it is about to expire.
const token = await auth.getAccessToken();
authorizedFetch envía Authorization: DPoP con una prueba nueva vinculada al token. Si el servidor solicita un nonce de DPoP, reintenta una vez con ese nonce. Las llamadas concurrentes comparten una única solicitud de renovación, y cada renovación rota el refresh token. Para verificar estos tokens en su API, consulte Verificación de tokens en su API.
Cerrar sesión
await auth.signOut();
signOut elimina los tokens y revoca el refresh token. Después envía el navegador al endpoint de cierre de sesión de su entorno, que lo devuelve a postLogoutRedirectUri.
Dónde se guardan los tokens
- Los access, refresh e ID tokens se guardan solo en memoria, nunca en localStorage, sessionStorage, IndexedDB, cookies ni en la URL. Tras recargar la página, el usuario vuelve a iniciar sesión mediante redirección; el SDK no restaura la sesión.
- sessionStorage solo guarda los valores de un solo uso de un inicio de sesión en curso.
- La clave DPoP es una clave WebCrypto no extraíble, que por defecto se mantiene en memoria.
Inicio de sesión integrado
El mismo objeto también proporciona a los componentes integrados un cliente de API frontal:
transactionspara los pasos de inicio de sesión;mepara el perfil de la cuenta yuser_metadata, correos electrónicos (con verificación de código), claves de acceso y sesiones;webauthnpara los procesos de clave de acceso.
Estas llamadas usan las cookies de su entorno y una cabecera CSRF. El servidor solo las acepta desde el propio origen de inicio de sesión del entorno.
Iniciar sesión sin redirección
En una página fuera de su origen de inicio de sesión, inicie la transacción con su clientId. El SDK la vincula entonces a su clave DPoP en lugar de a cookies:
let tx = await auth.transactions.create({ client_id: "app_01j9...", organization: "acme" });
tx = await auth.transactions.step("identifier", { identifier: "ada@acme.example" });
tx = await auth.transactions.step("password", { password });
if (tx.state === "completed") await auth.complete(tx); // { kind: "signed_in" }
- La aplicación debe ser una aplicación propia, y el origen de la página debe ser uno de sus orígenes web permitidos.
- Cada llamada incluye una prueba DPoP y ninguna cookie. Si el servidor solicita un nonce de DPoP, el SDK reintenta una vez con ese nonce.
- El SDK crea por sí mismo el verificador PKCE,
stateynonce, y los guarda en memoria.scopeyresourcese toman de las opciones; puede pasarorganizationyuiLocalesacreate. Use un único valor deresourceaquí. completeverifica la respuesta, intercambia el código de autorización con la misma clave DPoP y mantiene los tokens en memoria. La página no navega, y el código nunca se devuelve a su código.- Cada llamada a
step, con o sinclient_id, envía la zona horaria del navegador en la cabeceraIdp-Zoneinfo. Cuando el paso completa el inicio de sesión y el usuario no ha elegido una zona horaria, KeenAuth la guarda enzoneinfodel perfil, como lo hace la página de inicio de sesión alojada. Si el navegador no informa de una zona utilizable, la cabecera se omite y el paso no cambia. - Si el inicio de sesión necesita una página que el usuario debe ver (consentimiento, términos legales, elección de una organización), el último paso falla con un
KeenAuthProblemErrorcuyo código esauthorization_failed. Para ese usuario, usesignInRedirect.
Llamadas de cuenta desde su propio origen
Una vez que el usuario ha iniciado sesión y el SDK tiene un refresh token (el ámbito predeterminado solicita offline_access), las llamadas me y organizations ya no necesitan la cookie de sesión del host de inicio de sesión:
- El SDK renueva con
resource=<issuer>/fapiy obtiene un access token de corta duración para la API frontal. Está vinculado a DPoP y se mantiene solo en memoria, como los demás tokens. - Cada llamada de cuenta envía
Authorization: DPoP <token>y una prueba DPoP para esa llamada, sin cookie. Si el servidor solicita un nonce de DPoP, el SDK reintenta una vez con ese nonce; si el token ya no es válido, obtiene uno nuevo y reintenta una vez. - El refresh token rota en cada renovación, así que el SDK ejecuta las renovaciones una tras otra.
- Esto solo funciona para una aplicación propia cuyos orígenes web permitidos incluyan el origen de su página.
- Sin tokens, el SDK usa la cookie de sesión como antes.
Consulte Access token de la API frontal para las reglas del servidor.
Cambiar de organización
organizations.switch(orgId) cambia al usuario que ha iniciado sesión a otra organización sin cargar una página. Pase el ID org_… de una organización obtenida de organizations.listMine():
const { data } = await auth.organizations.listMine();
await auth.organizations.switch(data[1].id);
- Necesita lo mismo que las llamadas de cuenta anteriores: un refresh token,
redirectUriy una aplicación propia cuyos orígenes web permitidos incluyan el origen de su página. - El SDK envía una solicitud de cambio con el access token de la API frontal, intercambia el código con una prueba de la misma clave y mantiene los tokens nuevos, cuyo
org_ides la organización. Los tokens que tenía se conservan hasta que todo lo anterior se complete. - Si la organización necesita una página que el usuario debe ver (MFA, SSO, consentimiento), la llamada falla con un
KeenAuthProblemErrorcuyo código esauthorization_failed. Para ese usuario, usesignInRedirect({ organization }).
Límites actuales
- Las escrituras de perfil (
me.update,me.updateMetadata) envíanIf-Matchcon elETagde la última lectura o escritura del perfil. Si el perfil cambió en otro lugar entretanto, la escritura falla con unKeenAuthProblemErrorcuyo código esprecondition_failed; vuelva a llamar ame.get()antes de reintentar. - Los números de teléfono todavía no están disponibles:
me.phonesesundefined.