Applogin Dashboard

Applogin — 통합 로그인

스크립트 하나로 앱에 Google 및 이메일/비밀번호 로그인을 추가하세요. 모든 인증 — 로그인, 가입, 이메일 확인, 비밀번호 재설정, Google — 은 applogin.one의 보호된 iframe 안에서 이루어집니다. 여러분의 페이지는 사용자의 비밀번호를 전혀 볼 수 없고 아무것도 검증하지 않습니다. 받는 것은 바로 사용할 수 있는 앱 전용 토큰뿐입니다.

플랫폼이 대신 해주는 것
  • 로그인/가입 폼 렌더링(자체 iframe 안에서) — 직접 만들 필요가 없습니다.
  • 비밀번호 수신, 자격 증명 확인, 이메일 코드 발송과 검증 — 여러분은 아무것도 검증하지 않습니다.
  • 이메일당 하나의 계정 유지(Google과 비밀번호는 같은 계정에 연결).
  • 앱 간 세션을 유지하고 여러분의 앱 사이에서 로그인을 자동으로 이어받습니다.

작동 방식

Applogin은 인증 제공자입니다(Auth0/Clerk처럼). 다만 로그인 폼은 우리 도메인의 프레임에서 제공됩니다. 여러분의 앱은 SDK로 그 프레임을 모달로 열고 결과를 postMessage로 받습니다. 토큰은 여러분 애플리케이션의 시크릿으로 서명된 JWT(aud=app_id)라서 다른 애플리케이션에서는 쓸 수 없습니다.

여러분의 앱 (예: emailwriter.online) │ applogin.js + applogin-ui.js (SDK / 위젯) │ iframe이 든 모달을 연다: ▼ ┌──────────────────────────────────────────┐ │ iframe → https://applogin.one/login │ ← 비밀번호는 여기서 입력 │ (폼: Google / 이메일+비밀번호 / 재설정) │ 여러분의 JS는 못 봄 └──────────────────────────────────────────┘ ▲ postMessage { token, user } (여러분의 오리진에만) │ ▼ 앱 토큰을 받음 → 백엔드에서 검증 (/v1/session/verify)
개념설명
애플리케이션(app)플랫폼의 엔티티. 공개 app_id, jwt_secret(백엔드 토큰 검증), allowed_origins / redirect_uris 목록을 가집니다.
엔드 유저플랫폼의 모든 앱이 공유하는 글로벌 계정(이메일당 하나). Google과 비밀번호는 같은 계정에 연결됩니다.
앱 토큰앱의 jwt_secret으로 서명된 JWT(HS256, aud=app_id). 취소 가능한 세션이 뒷받침합니다.
SSO 세션applogin.one의 HttpOnly+Partitioned 쿠키 al_sso에 담긴 글로벌 세션. 같은 최상위 사이트 안에서 공유됩니다.

빠른 시작

스크립트 두 개를 추가하고 위젯을 마운트하세요. 조용히 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 설정입니다(앱 만들기 참조).

스크립트 URL의 ?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 })를 호출합니다. 토큰은 localStorageal_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를 설정합니다. 파티션 키는 최상위 사이트(가장 바깥쪽)입니다. 따라서:

const s = await Applogin.getSession();   // ← {authenticated:true,…} if a session exists in this partition
if (s.authenticated) showApp(s.user);
allowed_origins 요건. 로그인 프레임은 조상 체인 전체(frame-ancestors)를 확인하므로, 앱의 allowed_origins에는 자체 도메인과 래퍼 도메인(예: appdock.pro) 모두 있어야 합니다.
격리에 대해 솔직히. "같은 사이트 → SSO, 다른 최상위 → 재로그인" 구분은 브라우저(쿠키 파티셔닝, CHIPS)가 강제합니다. CHIPS 미지원 브라우저에서는 쿠키가 공유처럼 동작할 수 있어, 앱의 독립 페이지에서도 자동 로그인이 일어날 수 있습니다. 이는 구멍이 아닙니다: 토큰 발급은 여전히 allowed_origins로 게이트됩니다(무관한 사이트는 토큰을 얻지 못합니다).

언어

호스티드 로그인 폼은 10개 언어를 지원합니다: English, Deutsch, Français, Español, Italiano, Português, 日本語, 한국어, 中文, Русский. 기본적으로 사용자의 브라우저 언어로 열리며(영어 폴백), 폼 안에서 바로 언어를 바꿀 수 있습니다 — 하단의 국기 메뉴입니다.

내부적으로 폼 URL은 ?lang=xx(위 목록의 두 글자 코드)를 받습니다 — SDK가 대신 전달합니다.

라이브 예시: AppDock

appdock.pro는 "독" 런처입니다: 한 페이지에서 여러 앱을 iframe으로 엽니다. 앱들은 각자 자신의 도메인에 있습니다:

애플리케이션도메인app_id
Email Writeremailwriter.onlineapp_emailwriter
Audio Recorderaudiorecorder.infoapp_recorder

독 안에서 Email Writer에 로그인한 뒤 (역시 독 안의) Audio Recorder를 열면 로그인이 자동으로 이어집니다. 둘 다 같은 최상위 사이트 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"]
결과 동작: 독 안에서는 Email Writer와 Audio Recorder가 하나의 로그인을 공유합니다. 각 앱을 단독으로 열면(자체 도메인이 최상위) 새로 로그인합니다. 앱들 자체에는 로그인 폼이 전혀 없고 ApploginUI.mount만 있습니다.

백엔드에서 토큰 검증

프런트엔드는 요청의 Authorization 헤더에 Applogin.getToken()을 넣습니다. 백엔드 검증 방법은 두 가지입니다.

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_url을 설정하면, Google 로그인이 성공할 때마다 검증된 신원이 담긴 서명된 POST가 그곳으로 전송됩니다 — 토큰에 더해 서버 측 동기화를 위해서입니다.

헤더
X-Applogin-Timestampunix-ms
X-Applogin-Signaturetimestamp + "." + rawBodysha256=<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_urisGoogle 로그인용: https://applogin.one/auth-callback.html.
webhook_url(선택) 로그인 웹훅을 보낼 곳.

받는 것: app_id(공개), jwt_secret(백엔드 토큰 검증), client_secret, webhook_signing_secret.

REST API — 레퍼런스

베이스: https://applogin.one/api. 아래는 개발자에게 실제로 필요한 것입니다. 이메일/비밀번호와 Google 교환 엔드포인트는 호스티드 폼이 직접 호출하며, 여러분은 호출하지 않습니다.

개발자용

POST/v1/session/verify

백엔드에서 앱 토큰 검증(취소 반영). { app_id, token }{ valid, user }.

GET/v1/app/public

앱의 공개 데이터(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/exchangeGoogle 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"}

보안

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으로 서명.

Applogin · 통합 로그인 · 대시보드 · 라이브 예시: AppDock