Guía de integración
Añade «Iniciar con Tutu» a tu producto en cuatro pasos con OAuth2 + PKCE.
Cuatro pasos para integrar
El frontend inicia el login
Crea una instancia con createTutuId({ baseUrl, clientId }) de @tutu/id-sdk y llama a idSdk.startLogin() al hacer clic. El SDK genera un par PKCE y redirige a la página de autorización de TutuId.
El usuario autoriza en TutuId
En la pantalla de consentimiento /oauth/authorize el usuario pulsa «Permitir» (se omite si ya autorizó). Tras validar client_id y la lista blanca de redirect_uri, TutuId vuelve a tu /auth/callback con un código de autorización.
La página de callback obtiene el code
En /auth/callback llama a idSdk.completeCallback() para obtener { code, codeVerifier }. Pasa esos dos valores a tu propio backend; nunca intercambies el token directamente en el navegador.
El backend intercambia el token → crea la cuenta
Tu backend intercambia code + codeVerifier por un access_token, llama a /userinfo para obtener { sub, email, emailVerified, ... }, mapea o crea una cuenta local por sub y luego emite tu propia sesión.
★ Defensa contra apropiación de cuentas (obligatorio)
Tu backend solo puede vincular o crear una cuenta local por sub (el ID único de cuenta). Solo cuando emailVerified === true puedes vincular una cuenta local existente por correo; si no, crea una cuenta aparte y deja el correo vacío.
Por qué: un atacante podría preregistrar una cuenta Tutu con el correo de una víctima; fusionar por un correo no verificado entregaría la cuenta local de la víctima. Es una línea roja de nivel CVE.
Referencia de la API del SDK
@tutu/id-sdk · totalmente del lado del cliente, sin dependencias (requiere Web Crypto + sessionStorage).
createTutuId({ baseUrl, clientId, redirectUri? }) Crea una instancia del SDK. redirectUri por defecto es ${location.origin}/auth/callback, adaptándose a dev/prod.
startLogin(scope?) Genera un par PKCE y redirige a la página de autorización, iniciando «Iniciar con Tutu».
trySilentLogin(scope?) Inicio silencioso: si hay sesión central y autorización previa, intercambia un código sin fricción; si no, vuelve al estado de sesión cerrada de inmediato (sin interrumpir al usuario).
completeCallback() Analiza el resultado en la página de callback: en éxito devuelve { code, codeVerifier }, en fallo/cancelación devuelve { error }.
logoutEverywhere(returnTo?) Cierra la sesión central, propagando el cierre a todos los productos de la suite, y luego vuelve a returnTo.
¿Quieres verlo en acción primero?