Applogin — inicio de sesión unificado
Añade el inicio de sesión con Google y correo/contraseña a tu aplicación con un solo script. Toda la autenticación — inicio de sesión, registro, verificación de correo, restablecimiento de contraseña, Google — ocurre dentro de un iframe protegido en applogin.one. Tu página nunca ve la contraseña del usuario y no valida nada: solo recibes un token limitado a tu app listo para usar.
- Pinta el formulario de inicio de sesión/registro (en su propio iframe) — tú no construyes ninguno.
- Recibe la contraseña, comprueba las credenciales, envía y verifica los códigos por correo — tú no verificas nada.
- Mantiene una cuenta por correo (Google y contraseña apuntan a la misma cuenta).
- Mantiene una sesión entre aplicaciones y retoma el inicio de sesión entre tus apps en silencio.
Cómo funciona
Applogin es un proveedor de autenticación (como Auth0/Clerk), pero el formulario se sirve en un frame desde nuestro dominio. Tu app abre ese frame (en un modal) mediante el SDK y recibe el resultado por postMessage. El token es un JWT firmado con el secreto de tu aplicación (aud=app_id), por lo que es inútil en cualquier otra aplicación.
| Concepto | Qué es |
|---|---|
| Aplicación (app) | Una entidad de la plataforma. Tiene un app_id público, un jwt_secret (verificación del token en el backend) y las listas allowed_origins / redirect_uris. |
| Usuario final | Una cuenta global (una por correo) compartida por todas las aplicaciones de la plataforma. Google y contraseña se vinculan a la misma cuenta. |
| Token de app | JWT (HS256, aud=app_id) firmado con el jwt_secret de tu app. Respaldado por una sesión revocable. |
| Sesión SSO | Sesión global en la cookie HttpOnly+Partitioned al_sso en applogin.one. Compartida dentro de un mismo sitio de nivel superior. |
Inicio rápido
Incluye dos scripts y monta el widget: intenta el SSO en silencio, muestra un botón «Iniciar sesión», abre el formulario alojado en un frame y devuelve el usuario.
<script src="https://applogin.one/applogin.js?v=1"></script>
<script src="https://applogin.one/applogin-ui.js?v=1"></script>
<script>
ApploginUI.mount({
appId: "app_your_id",
onAuth: (user) => renderApp(user), // signed in: { id, email, name, picture, email_verified }
onLogout: () => showLanding()
});
// sign-out button: ApploginUI.logout();
</script>
Eso es todo. El formulario, el registro, los códigos por correo, Google y el restablecimiento de contraseña los gestiona Applogin. El único requisito: registrar un app_id e indicar tus allowed_origins (ver Crear una aplicación).
?v=1 en las URL de los scripts evita que el navegador sirva un SDK obsoleto de la caché tras una actualización.El widget ApploginUI recomendado
Un «candado» listo para tu app: mientras no haya sesión, muestra una pantalla con el botón «Iniciar sesión»; al hacer clic abre el formulario alojado en un modal; tras el inicio llama a onAuth(user) y quita la pantalla.
| Método | Descripción |
|---|---|
ApploginUI.mount({ appId, onAuth, onLogout, title? }) | Montar. Intenta el SSO silencioso; si no hay sesión, pinta la pantalla «Iniciar sesión». |
ApploginUI.logout() | Cerrar sesión (revoca la sesión y vuelve a la pantalla). |
ApploginUI.getUser() | El usuario actual o null. |
El perfil (avatar, nombre, correo) y el botón de salir los pintas tú desde onAuth(user) — esa es tu UI. Los datos del usuario llegan listos.
Métodos del SDK (applogin.js) bajo nivel
Si quieres tu propia UX en vez del widget, usa el SDK directamente. Llama primero a Applogin.init({ appId }). El token se guarda en localStorage bajo al_token_<appId>.
| Método | Descripción | Devuelve |
|---|---|---|
init({ appId }) | Inicialización. | — |
getSession() | SSO silencioso: retoma la sesión si el usuario inició en otra app del mismo sitio. | { authenticated, token, user } |
openLogin() | Abrir el modal del formulario alojado (iframe en applogin.one). | { token, user, lang } o null |
verify() | Comprobar el token actual. | { valid, user } |
logout() | Cerrar sesión (revoca la sesión y limpia el token). | — |
getToken() | El token de app actual. | string | null |
Applogin.init({ appId: "app_xxx" });
// on load — silently try SSO:
const s = await Applogin.getSession();
if (s.authenticated) showApp(s.user);
else {
const r = await Applogin.openLogin(); // modal with the applogin.one form
if (r) showApp(r.user);
}
// attach Applogin.getToken() to your backend requests
Inicio de sesión único (SSO)
Al iniciar sesión, Applogin pone la cookie al_sso con el atributo Partitioned. La clave de partición es el sitio de nivel superior (el más externo). Por lo tanto:
- Varias de tus apps bajo un mismo sitio (p. ej. incrustadas en
appdock.pro) comparten la sesión →getSession()en la segunda app retoma el inicio sin formulario. - La misma app abierta como sitio aparte (otro nivel superior) es otra partición → el usuario inicia de nuevo.
const s = await Applogin.getSession(); // ← {authenticated:true,…} if a session exists in this partition
if (s.authenticated) showApp(s.user);
allowed_origins de una app deben listar su propio dominio y el dominio contenedor (p. ej. appdock.pro).allowed_origins (un sitio ajeno nunca obtiene un token).Idiomas
El formulario de inicio alojado habla 10 idiomas: English, Deutsch, Français, Español, Italiano, Português, 日本語, 한국어, 中文, Русский. Por defecto se abre en el idioma del navegador del usuario (con inglés como respaldo); el idioma se cambia directamente en el formulario — el menú de banderas abajo.
- Forzar un idioma. Pasa
langal widget o al SDK:ApploginUI.mount({ appId, lang: "es" })oApplogin.init({ appId, lang: "es" })— el formulario se abre en ese idioma. Sin la opción, el SDK envía automáticamente el idioma del navegador. - Leer la elección. El usuario puede cambiar el idioma dentro del formulario. El resultado de
Applogin.openLogin()contienelang— el idioma del formulario al iniciar sesión — para que tu página pueda seguirlo. El widget también lo guarda (localStorageal_lang), y el próximo formulario se abrirá en él. - Los correos siguen al formulario. Los correos de verificación y restablecimiento se envían en el idioma del formulario.
Por debajo, la URL del formulario acepta ?lang=xx (código de dos letras de la lista de arriba) — el SDK lo pasa por ti.
Ejemplo en vivo: AppDock
appdock.pro es un lanzador tipo «dock»: una página que abre varias aplicaciones en iframes. Las aplicaciones viven en sus propios dominios:
| Aplicación | Dominio | app_id |
|---|---|---|
| Email Writer | emailwriter.online | app_emailwriter |
| Audio Recorder | audiorecorder.info | app_recorder |
Inicia sesión en Email Writer dentro del dock y luego abre Audio Recorder (también dentro del dock): el inicio se retoma automáticamente, porque ambos corren bajo el mismo sitio de nivel superior appdock.pro y comparten una partición SSO.
1. El lanzador (appdock.pro)
<button class="tile" data-src="https://emailwriter.online/">Email Writer</button>
<button class="tile" data-src="https://audiorecorder.info/">Audio Recorder</button>
<iframe id="frame" allow="microphone; clipboard-write"></iframe>
<script>
document.querySelectorAll(".tile").forEach(t =>
t.onclick = () => document.getElementById("frame").src = t.dataset.src);
</script>
2. Una aplicación (p. ej. emailwriter.online)
<script src="https://applogin.one/applogin.js?v=1"></script>
<script src="https://applogin.one/applogin-ui.js?v=1"></script>
<script>
ApploginUI.mount({
appId: "app_emailwriter",
title: "Email Writer — sign in",
onAuth: (user) => { // render the profile + the app
header.textContent = user.name + " · " + user.email;
app.hidden = false;
},
onLogout: () => { app.hidden = true; }
});
</script>
3. Registro de apps para este escenario
Cada aplicación lista en allowed_origins su propio dominio + el del dock (frame-ancestors ve toda la cadena de ancestros del frame anidado):
app_emailwriter → allowed_origins: ["https://emailwriter.online", "https://appdock.pro"]
app_recorder → allowed_origins: ["https://audiorecorder.info", "https://appdock.pro"]
// redirect_uris (for Google): ["https://applogin.one/auth-callback.html"]
ApploginUI.mount.Verificar el token en tu backend
Tu frontend pone Applogin.getToken() en la cabecera Authorization de sus peticiones. En el backend hay dos formas de verificarlo.
1. Con el endpoint de la plataforma considera la revocación
POST https://applogin.one/api/v1/session/verify
{ "app_id": "app_xxx", "token": "<JWT from Authorization>" }
// → { "valid": true, "user": { "id": 2, "email": "…", "email_verified": true, "name": "…", "picture": null } }
// invalid/revoked (logout) → { "valid": false, "user": null }
2. En local (HS256 con tu jwt_secret) sin red
// Node.js
const jwt = require("jsonwebtoken");
const claims = jwt.verify(token, process.env.APPLOGIN_JWT_SECRET, {
algorithms: ["HS256"], audience: "app_xxx", issuer: "applogin"
});
// claims.sub — user id, claims.email, claims.email_verified
# Python
import jwt
claims = jwt.decode(token, APPLOGIN_JWT_SECRET, algorithms=["HS256"],
audience="app_xxx", issuer="applogin")
/v1/session/verify.Estructura del JWT
{
"iss": "applogin",
"aud": "app_xxx", // your app_id
"sub": "2", // user id (as a string)
"jti": "…", // session id (for revocation)
"email": "user@example.com",
"email_verified": true,
"name": "Jane", "picture": null,
"iat": 1782194619, "exp": 1784786619 // ~30 days
}
Webhook (opcional)
Si una aplicación define un webhook_url, la plataforma le envía un POST firmado con la identidad verificada en cada inicio con Google — para sincronización en el servidor además del token.
| Cabecera | Valor |
|---|---|
X-Applogin-Timestamp | unix-ms |
X-Applogin-Signature | sha256=<hex> de timestamp + "." + rawBody |
const exp = "sha256=" + crypto.createHmac("sha256", WEBHOOK_SIGNING_SECRET)
.update(ts + "." + rawBody).digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(exp), Buffer.from(sig))) reject();
Crear una aplicación
Las aplicaciones se crean en el panel privado applogin.one/dashboard (inicio de sesión con el propio Applogin). Debes indicar:
| Campo | Para qué |
|---|---|
name | Se muestra en el formulario («Iniciar sesión · <name>»). |
allowed_origins | Orígenes autorizados a incrustar el formulario y recibir tokens. Lista el dominio de la app + el dominio contenedor (p. ej. el dock). |
redirect_uris | Para el inicio con Google: https://applogin.one/auth-callback.html. |
webhook_url | (opcional) adónde enviar el webhook de inicio. |
Recibes: app_id (público), jwt_secret (verificación del token en el backend), client_secret, webhook_signing_secret.
API REST — referencia
Base: https://applogin.one/api. Abajo, lo que un integrador realmente necesita. Los endpoints de correo/contraseña y de intercambio de Google los llama el propio formulario alojado — tú nunca.
Para el integrador
Verificar un token de app en tu backend (con revocación). { app_id, token } → { valid, user }.
Datos públicos de la aplicación (name, allowed_origins). Los usa la página de inicio alojada.
Internos (los llama el formulario alojado/SDK, no tú)
| Endpoint | Rol |
|---|---|
POST /v1/email/register · verify · resend · login · reset/request · reset/confirm · password/change | correo/contraseña — llamados desde el formulario alojado /login. |
POST /v1/auth/start, GET /oauth/google/callback, POST /v1/auth/exchange | OAuth de Google (dentro del formulario alojado). |
POST /v1/session/sso | SSO silencioso (lo llama /session desde un iframe oculto). |
POST /v1/session/establish | Pone la cookie SSO en la partición correcta (después de Google). |
POST /v1/session/logout | Revocación de la sesión (mediante el SDK logout()). |
Errores
| HTTP | Cuándo |
|---|---|
| 400 | Entrada no válida (contraseña corta, código inválido/caducado, «Email already registered»). |
| 403 | «Invalid credentials», acceso denegado, «Too many attempts» (límite de intentos). |
| 404 | Aplicación/ruta desconocida. |
{"error":"Invalid or expired code","message":"Invalid or expired code"}
Seguridad
- La contraseña está aislada. Los campos de entrada existen solo dentro del iframe de applogin.one; el JS del sitio integrador no puede alcanzarlos.
- Tokens limitados a la app. El JWT está firmado con el secreto de la aplicación y lleva
aud=app_id— el token de una app es inválido en otra. - Entrega del token solo a tus orígenes. El formulario envía el token por
postMessagesolo a un origen deallowed_origins; la página de inicio se sirve conContent-Security-Policy: frame-ancestors <allowed_origins>(anti-clickjacking). - La sesión global es HttpOnly. La cookie
al_ssoes inaccesible desde JS; el integrador solo tiene un token de app. - Anti-CSRF en el SSO.
/v1/session/ssoemite un token solo paraOrigin ∈ allowed_origins— un sitio ajeno con la cookie del usuario no obtiene nada. - Anti-fuerza bruta. Límites de intentos en login/reset/verify (por correo y por IP).
- Las contraseñas se guardan con hash (PBKDF2); los códigos con hash, TTL de 10 minutos y límite de intentos; HTTPS en todas partes; los tokens de Google nunca se almacenan. Inicio con Google mediante PKCE (S256) + state + nonce.
Chuleta para LLM
Modelo: toda la autenticación ocurre en un iframe en applogin.one. El integrador NO pinta formularios, NO envía contraseñas, NO verifica códigos. Incluye SDK + widget y recibe un token de app.
// include
<script src=https://applogin.one/applogin.js?v=1></script>
<script src=https://applogin.one/applogin-ui.js?v=1></script>
// mount (a lock on the app)
ApploginUI.mount({ appId, onAuth:(user)=>..., onLogout:()=>... });
ApploginUI.logout();
// low level
Applogin.init({ appId });
await Applogin.getSession(); // {authenticated, token, user} — silent SSO
await Applogin.openLogin(); // {token, user, lang}|null — modal with the form
Applogin.getToken(); await Applogin.verify(); await Applogin.logout();
// backend: verify the token
POST https://applogin.one/api/v1/session/verify {app_id, token} -> {valid, user}
// or locally: jwt.verify(token, jwt_secret, {algorithms:["HS256"], audience:app_id, issuer:"applogin"})
SSO: el inicio pone la cookie al_sso (Partitioned, por sitio de nivel superior). Las apps bajo un mismo sitio (p. ej. iframes de un dock como appdock.pro) comparten la sesión; un dominio aparte → inicio nuevo. Los allowed_origins de una app = su dominio + el dominio contenedor.
El token está limitado a la app: iss=applogin, aud=app_id, sub=id de usuario, firmado con el jwt_secret de la app.
Applogin · inicio de sesión unificado · panel · ejemplo en vivo: AppDock