Applogin — 統合サインイン
スクリプト1つで、アプリに Google とメール/パスワードのサインインを追加できます。すべての認証 — サインイン、登録、メール確認、パスワードリセット、Google — は applogin.one 上の保護された iframe 内で行われます。あなたのページはユーザーのパスワードを一切見ることなく、何も検証しません。受け取るのは、すぐ使えるアプリ限定トークンだけです。
- サインイン/登録フォームの描画(独自の iframe 内)— あなたが作る必要はありません。
- パスワードの受け取り、認証情報の確認、メールコードの送信と検証 — あなたは何も検証しません。
- メールアドレスごとに1つのアカウントを管理(Google とパスワードは同じアカウントに紐づく)。
- アプリ間セッションを維持し、あなたのアプリ間でサインインを自動的に引き継ぎます。
仕組み
Applogin は認証プロバイダー(Auth0/Clerk のような)ですが、ログインフォームは当社ドメインのフレーム内で提供されます。あなたのアプリは SDK を通じてそのフレームをモーダルで開き、結果を postMessage で受け取ります。トークンはあなたのアプリケーションの秘密鍵で署名された JWT(aud=app_id)で、他のアプリケーションでは無効です。
| 概念 | 説明 |
|---|---|
| アプリケーション (app) | プラットフォーム上のエンティティ。公開の app_id、jwt_secret(バックエンドでのトークン検証用)、allowed_origins / redirect_uris のリストを持ちます。 |
| エンドユーザー | プラットフォームの全アプリで共有されるグローバルアカウント(メールごとに1つ)。Google とパスワードは同じアカウントに紐づきます。 |
| アプリトークン | あなたのアプリの jwt_secret で署名された JWT(HS256、aud=app_id)。失効可能なセッションに裏付けられています。 |
| SSO セッション | applogin.one 上の HttpOnly+Partitioned クッキー al_sso に入るグローバルセッション。同じトップレベルサイト内で共有されます。 |
クイックスタート
スクリプトを2つ読み込んでウィジェットをマウントするだけ。静かに SSO を試し、「サインイン」ボタンを表示し、ホスト型フォームをフレームで開いてユーザーを返します。
<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 は、更新後にブラウザがキャッシュ済みの古い SDK を返すのを防ぎます。ApploginUI ウィジェット 推奨
アプリの「鍵」として使える既製品:未サインイン時は「サインイン」ボタン付きの画面を表示し、クリックでホスト型フォームをモーダルで開き、サインイン後は onAuth(user) を呼んで画面を外します。
| メソッド | 説明 |
|---|---|
ApploginUI.mount({ appId, onAuth, onLogout, title? }) | マウント。静かに SSO を試し、未サインインなら「サインイン」画面を表示。 |
ApploginUI.logout() | サインアウト(セッションを失効し、画面を再表示)。 |
ApploginUI.getUser() | 現在のユーザー、または null。 |
プロフィール(アバター、名前、メール)とサインアウトボタンは onAuth(user) からあなたが描画します — それがあなたの UI です。ユーザーデータはそのまま使えます。
SDK メソッド(applogin.js)低レベル
ウィジェットではなく独自の UX が欲しい場合は SDK を直接使います。まず Applogin.init({ appId }) を呼びます。トークンは localStorage の al_token_<appId> に保存されます。
| メソッド | 説明 | 戻り値 |
|---|---|---|
init({ appId }) | 初期化。 | — |
getSession() | 静かな SSO:同じサイトの別アプリでサインイン済みならセッションを引き継ぐ。 | { authenticated, token, user } |
openLogin() | ホスト型ログインフォームのモーダルを開く(applogin.one 上の iframe)。 | { token, user, lang } または null |
verify() | 現在のトークンを検証。 | { valid, user } |
logout() | サインアウト(セッション失効+トークン削除)。 | — |
getToken() | 現在のアプリトークン。 | 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 は Partitioned 属性付きのクッキー al_sso を設定します。パーティションのキーはトップレベルサイト(最も外側)です。したがって:
- 同じサイトの下にある複数のアプリ(例:
appdock.proに埋め込まれたもの)はセッションを共有 → 2つ目のアプリのgetSession()がフォームなしでサインインを引き継ぎます。 - 同じアプリを別サイトとして開く(別のトップレベル)と別パーティション → ユーザーは再度サインインします。
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 でゲートされます(無関係なサイトはトークンを得られません)。言語
ホスト型ログインフォームは10言語に対応しています:English, Deutsch, Français, Español, Italiano, Português, 日本語, 한국어, 中文, Русский。デフォルトではユーザーのブラウザ言語で開き(フォールバックは英語)、フォーム内で言語を切り替えられます — 下部の国旗メニューです。
- 言語を指定する。ウィジェットまたは SDK に
langを渡します:ApploginUI.mount({ appId, lang: "ja" })またはApplogin.init({ appId, lang: "ja" })— フォームはその言語で開きます。オプションなしの場合、SDK はブラウザ言語を自動で送ります。 - 選択を受け取る。ユーザーはフォーム内で言語を変更できます。
Applogin.openLogin()の結果にはlang(サインイン時のフォームの言語)が含まれ、ページ側で追従できます。ウィジェットは localStorage のal_langにも保存し、次回のフォームはその言語で開きます。 - メールはフォームに従う。確認コードやパスワードリセットのメールはフォームの言語で送信されます。
内部的にはフォームの URL が ?lang=xx(上記リストの2文字コード)を受け付けます — SDK が自動で付与します。
ライブ例:AppDock
appdock.pro は「ドック」型ランチャー:1つのページから複数のアプリを iframe で開きます。アプリはそれぞれ独自ドメインにあります:
| アプリケーション | ドメイン | app_id |
|---|---|---|
| Email Writer | emailwriter.online | app_emailwriter |
| Audio Recorder | audiorecorder.info | app_recorder |
ドック内で Email Writer にサインインし、次に(同じくドック内の)Audio Recorder を開くと、サインインは自動的に引き継がれます。両方が同じトップレベルサイト appdock.pro の下で動き、1つの 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 だけです。バックエンドでのトークン検証
フロントエンドはリクエストの Authorization ヘッダーに Applogin.getToken() を入れます。バックエンドでの検証方法は2つです。
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. ローカルで(あなたの jwt_secret による HS256)ネット不要
// 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(任意)
アプリに webhook_url を設定すると、Google サインインの成功のたびに、検証済みの ID を含む署名付き POST がそこへ送られます — トークンに加えたサーバー側同期のためです。
| ヘッダー | 値 |
|---|---|
X-Applogin-Timestamp | unix-ms |
X-Applogin-Signature | timestamp + "." + rawBody の sha256=<hex> |
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 | フォームの埋め込みとトークン受信を許可するオリジン。アプリのドメイン+ラッパードメイン(例:ドック)を列挙。 |
redirect_uris | Google サインイン用:https://applogin.one/auth-callback.html。 |
webhook_url | (任意)サインイン Webhook の送信先。 |
受け取るもの:app_id(公開)、jwt_secret(バックエンドでのトークン検証)、client_secret、webhook_signing_secret。
REST API — リファレンス
ベース:https://applogin.one/api。以下は開発者が実際に必要とするものです。メール/パスワードや Google 交換のエンドポイントはホスト型フォーム自身が呼び、あなたは呼びません。
開発者向け
バックエンドでアプリトークンを検証(失効考慮)。{ app_id, token } → { valid, user }。
アプリの公開データ(name、allowed_origins)。ホスト型ログインページが使用。
内部用(ホスト型フォーム/SDK が呼ぶ。あなたは呼ばない)
| エンドポイント | 役割 |
|---|---|
POST /v1/email/register · verify · resend · login · reset/request · reset/confirm · password/change | メール/パスワード — ホスト型フォーム /login から呼ばれる。 |
POST /v1/auth/start, GET /oauth/google/callback, POST /v1/auth/exchange | Google OAuth(ホスト型フォーム内)。 |
POST /v1/session/sso | 静かな SSO(隠し iframe から /session が呼ぶ)。 |
POST /v1/session/establish | 正しいパーティションに SSO クッキーを設定(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"}
セキュリティ
- パスワードは隔離。入力フィールドは applogin.one の iframe 内にのみ存在し、埋め込み先サイトの JS からは届きません。
- アプリ限定トークン。JWT はアプリの秘密鍵で署名され
aud=app_idを持ちます — あるアプリのトークンは別のアプリでは無効です。 - トークンはあなたのオリジンにのみ配信。フォームは
allowed_originsのオリジンにのみpostMessageでトークンを送り、ログインページ自体はContent-Security-Policy: frame-ancestors <allowed_origins>付きで配信されます(クリックジャッキング対策)。 - グローバルセッションは HttpOnly。クッキー
al_ssoは JS から到達不能。開発者の手元にあるのはアプリトークンだけです。 - SSO の CSRF 対策。
/v1/session/ssoはOrigin ∈ allowed_originsのときだけトークンを発行 — ユーザーのクッキーを持つ無関係なサイトは何も得られません。 - ブルートフォース対策。login/reset/verify にレート制限(メール単位と IP 単位)。
- パスワードはハッシュ化(PBKDF2)。コードはハッシュ化・TTL 10 分・試行回数制限。全面 HTTPS。Google のトークンは保存しません。Google サインインは PKCE(S256)+ state + nonce。
LLM 向けチートシート
モデル:すべての認証は applogin.one の iframe 内で行われる。開発者はフォームを描画せず、パスワードを送らず、コードを検証しない。SDK + ウィジェットを読み込み、アプリトークンを受け取る。
// 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:サインインはクッキー al_sso(Partitioned、トップレベルサイト単位)を設定する。同じサイトの下のアプリ(例:appdock.pro のようなドックの iframe)はセッションを共有。別ドメイン → 再サインイン。アプリの allowed_origins = 自ドメイン + ラッパードメイン。
トークンはアプリ限定:iss=applogin、aud=app_id、sub=ユーザー ID、アプリの jwt_secret で署名。