Applogin — единая авторизация
Подключите вход через Google и email/пароль к своему приложению одним скриптом. Вся авторизация — вход, регистрация, подтверждение почты, сброс пароля, Google — происходит внутри защищённого iframe на applogin.one. Ваша страница никогда не видит пароль пользователя и ничего не проверяет — вы получаете только готовый app-scoped токен.
- Рисует форму входа/регистрации (в своём iframe) — вам не нужно её делать.
- Принимает пароль, проверяет учётные данные, шлёт и проверяет коды с почты — вам не нужно ничего верифицировать.
- Ведёт единый аккаунт пользователя (Google и пароль — один аккаунт).
- Держит сквозную сессию и молча подхватывает вход между вашими приложениями.
Как это работает
Applogin — провайдер аутентификации (как Auth0/Clerk), но форма входа отдаётся во фрейме с нашего домена. Ваше приложение через SDK открывает этот фрейм (во всплывашке) и получает результат по postMessage. Токен — JWT, подписанный секретом вашего приложения (aud=app_id), поэтому он бесполезен в чужом приложении.
| Понятие | Что это |
|---|---|
| Приложение (app) | Сущность на платформе. Имеет публичный app_id, jwt_secret (проверка токена на бэкенде) и списки allowed_origins / redirect_uris. |
| End user | Глобальный аккаунт (один на e-mail) для всех приложений платформы. Google и пароль линкуются к одному аккаунту. |
| App-токен | JWT (HS256, aud=app_id), подписан jwt_secret вашего приложения. За ним — отзываемая сессия. |
| SSO-сессия | Глобальная сессия в HttpOnly+Partitioned cookie al_sso на applogin.one. Общая в пределах одного top-level сайта. |
Быстрый старт
Подключите два скрипта и смонтируйте виджет — он сам сделает тихий вход, покажет кнопку «Войти», откроет форму во фрейме и вернёт пользователя.
<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>
Всё. Форму, регистрацию, коды на почту, Google и сброс пароля берёт на себя Applogin. Единственное условие — зарегистрировать app_id и указать allowed_origins (см. Создание приложения).
?v=1 в URL скриптов нужна, чтобы браузер не отдавал старую закэшированную версию SDK при обновлениях.Виджет ApploginUI рекомендуется
Готовый «замок» на приложение: пока не вошёл — показывает экран с кнопкой «Войти»; по клику открывает всплывашку с формой; после входа зовёт onAuth(user) и убирает экран.
| Метод | Описание |
|---|---|
ApploginUI.mount({ appId, onAuth, onLogout, title? }) | Смонтировать. Делает тихий SSO; если не вошёл — рисует лендинг «Войти». |
ApploginUI.logout() | Выход (отзыв сессии + снова лендинг). |
ApploginUI.getUser() | Текущий пользователь или null. |
Профиль (аватар, имя, e-mail) и кнопку «Выйти» рисуете вы из onAuth(user) — это ваш UI. Данные пользователя приходят готовыми.
Методы SDK (applogin.js) низкий уровень
Если нужен свой UX вместо виджета — работайте с SDK напрямую. Сначала Applogin.init({ appId }). Токен хранится в localStorage под ключом al_token_<appId>.
| Метод | Описание | Возвращает |
|---|---|---|
init({ appId }) | Инициализация. | — |
getSession() | Тихий SSO: подхватить сессию, если юзер вошёл в другом приложении того же сайта. | { authenticated, token, user } |
openLogin() | Открыть всплывашку с формой входа (iframe на applogin.one). | { token, user, lang } или null |
verify() | Проверить текущий токен. | { valid, user } |
logout() | Выход (отзыв сессии + очистка). | — |
getToken() | Текущий app-токен. | 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
Сквозной вход (SSO)
При входе Applogin ставит cookie al_sso с атрибутом Partitioned. Ключ партиции — top-level сайт (самый внешний). Поэтому:
- Несколько ваших приложений под одним сайтом (напр. встроенные в
appdock.pro) делят сессию →getSession()во втором приложении подхватывает вход без формы. - То же приложение, открытое как отдельный сайт (другой top-level) — другая партиция → вход попросят заново.
const s = await Applogin.getSession(); // ← {authenticated:true,…} if a session exists in this partition
if (s.authenticated) showApp(s.user);
allowed_origins приложения нужно перечислить и его собственный домен, и домен-обёртку (напр. appdock.pro).allowed_origins (чужой сайт токена не получит).Языки
Hosted-форма входа говорит на 10 языках: English, Deutsch, Français, Español, Italiano, Português, 日本語, 한국어, 中文, Русский. По умолчанию она открывается на языке браузера пользователя (фоллбек — английский); язык можно сменить прямо в форме — меню с флагами внизу.
- Задать язык явно. Передайте
langв виджет или SDK:ApploginUI.mount({ appId, lang: "ru" })илиApplogin.init({ appId, lang: "ru" })— форма откроется на этом языке. Без опции SDK автоматически отправляет язык браузера. - Узнать выбор пользователя. Пользователь может сменить язык внутри формы. Результат
Applogin.openLogin()содержитlang— язык формы на момент входа — чтобы ваша страница могла подстроиться. Виджет также сохраняет его (localStorageal_lang), и следующая форма откроется на нём. - Письма следуют за формой. Письма с кодами подтверждения и сброса пароля уходят на языке формы.
Под капотом URL формы принимает ?lang=xx (двухбуквенный код из списка выше) — SDK передаёт его за вас.
Живой пример: AppDock
appdock.pro — «док»-лаунчер: одна страница, которая открывает несколько приложений в iframe. Приложения живут на своих доменах:
| Приложение | Домен | app_id |
|---|---|---|
| Email Writer | emailwriter.online | app_emailwriter |
| Audio Recorder | audiorecorder.info | app_recorder |
Войдите в Email Writer внутри дока, затем откройте Audio Recorder (тоже внутри дока) — вход подхватится сам: оба работают под одним top-level сайтом appdock.pro и делят одну SSO-партицию.
1. Док-лаунчер (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. Приложение (напр. 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. Регистрация приложений для этого сценария
У каждого приложения в allowed_origins — свой домен + домен дока (frame-ancestors видит всю цепочку предков вложенного фрейма):
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.Проверка токена на вашем бэкенде
Фронт кладёт Applogin.getToken() в заголовок Authorization ваших запросов. На бэкенде токен проверяют двумя способами.
1. Ручкой платформы учитывает отзыв
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. Локально (HS256 вашим jwt_secret) без сети
// 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.Структура 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_url, платформа при успешном входе через Google шлёт на него подписанный POST с проверенной личностью — для серверной синхронизации в дополнение к токену.
| Заголовок | Значение |
|---|---|
X-Applogin-Timestamp | unix-ms |
X-Applogin-Signature | sha256=<hex> от 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();
Создание приложения
Приложения создаются в закрытом дашборде applogin.one/dashboard (вход — через сам Applogin). При создании вы указываете:
| Поле | Зачем |
|---|---|
name | Показывается в форме входа («Вход · <name>»). |
allowed_origins | Origin'ы, которым разрешено встраивать форму и получать токены. Укажите домен приложения + домен-обёртку (напр. дока). |
redirect_uris | Для Google-входа: https://applogin.one/auth-callback.html. |
webhook_url | (опц.) куда слать вебхук о входе. |
Выдаются: app_id (публичный), jwt_secret (для проверки токена на бэкенде), client_secret, webhook_signing_secret.
REST API — справочник
База: https://applogin.one/api. Ниже — что реально нужно билдеру. Ручки email/пароля и Google-обмена дёргает сама hosted-форма, вам их вызывать не нужно.
Для билдера
Проверить app-токен на бэкенде (с учётом отзыва). { app_id, token } → { valid, user }.
Публичные данные приложения (name, allowed_origins). Использует hosted-страница входа.
Внутренние (дёргает hosted-форма/SDK, не билдер)
| Ручка | Роль |
|---|---|
POST /v1/email/register · verify · resend · login · reset/request · reset/confirm · password/change | email/пароль — вызывается из hosted-формы /login. |
POST /v1/auth/start, GET /oauth/google/callback, POST /v1/auth/exchange | Google OAuth (внутри hosted-формы). |
POST /v1/session/sso | Тихий SSO (дёргает /session из скрытого iframe). |
POST /v1/session/establish | Ставит SSO-cookie в правильной партиции (после Google). |
POST /v1/session/logout | Отзыв сессии (через SDK logout()). |
Ошибки
| HTTP | Когда |
|---|---|
| 400 | Неверный ввод (короткий пароль, истёкший/неверный код, «Email already registered»). |
| 403 | «Invalid credentials», доступ запрещён, «Too many attempts» (рейт-лимит). |
| 404 | Неизвестное приложение/маршрут. |
{"error":"Invalid or expired code","message":"Invalid or expired code"}
Безопасность
- Пароль изолирован. Поля ввода — только в iframe на applogin.one; JS встраивающего сайта к ним не имеет доступа. Билдер не может «подслушать» пароль.
- App-scoped токены. JWT подписан секретом приложения и имеет
aud=app_id— токен одного приложения недействителен в другом. Билдер не зайдёт под своим юзером в чужое приложение. - Доставка токена только своим. Форма шлёт токен
postMessageтолько на origin изallowed_origins; сама страница входа отдаётся сContent-Security-Policy: frame-ancestors <allowed_origins>(анти-clickjacking). - Глобальная сессия — HttpOnly. Cookie
al_ssoнедоступна JS; у билдера на руках только app-токен. - Анти-CSRF на SSO.
/v1/session/ssoвыдаёт токен только дляOrigin ∈ allowed_origins— чужой сайт с cookie юзера токена не получит. - Анти-брутфорс. Рейт-лимит на login/reset/verify (по e-mail и IP).
- Пароли — хешируются (PBKDF2); коды — хеш, TTL 10 мин, лимит попыток; HTTPS везде; Google-токены не храним. Google-вход — PKCE (S256) + state + nonce.
Шпаргалка для LLM
Модель: вся авторизация — в iframe на applogin.one. Билдер НЕ рисует форму, НЕ шлёт пароль, НЕ проверяет коды. Билдер подключает SDK + виджет и получает 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: вход ставит cookie al_sso (Partitioned, по top-level сайту). Приложения под одним сайтом (напр. в iframe дока appdock.pro) делят сессию; отдельный домен → вход заново. В allowed_origins приложения — его домен + домен-обёртка.
Токен app-scoped: iss=applogin, aud=app_id, sub=user id, подписан jwt_secret приложения.
Applogin · единая авторизация · дашборд · живой пример: AppDock