const CSS = `
:root {
color-scheme: light;
--bg: #f3efe4;
--ink: #1b1916;
--muted: #5c564c;
--accent: #0b5d45;
--card: #fffdf7;
--line: #d9d2c3;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
font-family: "Iowan Old Style", Palatino, Georgia, serif;
background: var(--bg);
color: var(--ink);
line-height: 1.45;
}
main { max-width: 36rem; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; }
h1 { font-size: 1.75rem; letter-spacing: -0.02em; margin: 0 0 0.5rem; }
p { color: var(--muted); margin: 0 0 1rem; }
a { color: var(--accent); }
button, .btn {
appearance: none;
border: 0;
background: var(--accent);
color: #f7fff9;
font: 600 0.95rem/1.2 system-ui, sans-serif;
padding: 0.7rem 1rem;
border-radius: 999px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
button.secondary { background: transparent; color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
.wordmark { font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent); margin-bottom: 1.25rem; }
.list { display: grid; gap: 0.6rem; margin: 1.25rem 0; }
.account {
text-align: left;
width: 100%;
border-radius: 14px;
background: var(--card);
color: var(--ink);
box-shadow: inset 0 0 0 1px var(--line);
padding: 0.85rem 1rem;
font-family: system-ui, sans-serif;
}
.account strong { display: block; font-size: 1rem; }
.account span { color: var(--muted); font-size: 0.8rem; }
.empty { font-family: system-ui, sans-serif; color: var(--muted); }
.actions { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-top: 1.25rem; }
.mono { font-family: ui-monospace, monospace; font-size: 0.8rem; word-break: break-all; }
`;
/**
* JSON literal safe to inline in a `` from closing the element early.
*/
function jsonForScript(value: unknown): string {
return JSON.stringify(value ?? null)
.replaceAll("<", "\\u003c")
.replaceAll(">", "\\u003e")
.replaceAll("\u2028", "\\u2028")
.replaceAll("\u2029", "\\u2029");
}
function shell(title: string, body: string, extraHead = ""): string {
return `
${escapeHtml(title)}
${extraHead}
${body}
`;
}
export { landingPage } from "./landing";
export function embedPage(): string {
const script = `
const parentOrigin = resolveParentOrigin();
function resolveParentOrigin() {
if (location.ancestorOrigins && location.ancestorOrigins[0]) return location.ancestorOrigins[0];
if (document.referrer) {
try { return new URL(document.referrer).origin; } catch { return ""; }
}
return "";
}
function isHttpOrigin(origin) {
try {
const url = new URL(origin);
if (url.protocol === "https:") return true;
return url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1");
} catch {
return false;
}
}
function hasAccess() {
if (window.top === window) return Promise.resolve(true);
if (typeof document.hasStorageAccess === "function") return document.hasStorageAccess();
return Promise.resolve(true);
}
async function tryActivateExistingGrant() {
if (window.top === window) return true;
if (typeof document.requestStorageAccess !== "function") return false;
try {
await document.requestStorageAccess();
return true;
} catch {
return false;
}
}
async function api(method, path, body) {
const res = await fetch(path, {
method,
credentials: "include",
headers: body ? { "content-type": "application/json" } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.message || res.statusText);
return data;
}
function showChoose() {
window.parent.postMessage({ type: "needs-access" }, parentOrigin);
document.body.innerHTML = 'Continue with an AT Proto account you already use ';
document.body.style.margin = "0";
document.body.style.background = "transparent";
const btn = document.getElementById("choose");
btn.style.width = "100%";
btn.addEventListener("click", async () => {
// Do not call hasStorageAccess() here — it can consume the user gesture.
if (typeof document.requestStorageAccess === "function") {
try {
await document.requestStorageAccess();
window.location.reload();
return;
} catch {}
}
window.parent.postMessage({ type: "popup" }, parentOrigin);
const url = "/choose?parent=" + encodeURIComponent(parentOrigin);
window.open(url, "loggedin-choose", "width=440,height=600");
});
}
async function bootSilent() {
window.addEventListener("message", onMessage);
window.parent.postMessage({ type: "ready" }, parentOrigin);
const data = await api("GET", "/xrpc/fyi.loggedin.account.list");
window.parent.postMessage({ type: "accounts", accounts: data.accounts }, parentOrigin);
}
async function boot() {
if (!parentOrigin || !isHttpOrigin(parentOrigin)) return;
if (await hasAccess()) {
await bootSilent();
return;
}
if (await tryActivateExistingGrant()) {
window.location.reload();
return;
}
showChoose();
}
async function onMessage(event) {
if (event.data && event.data.type === "picked" && event.data.did && event.source === window) {
return;
}
if (event.data && event.data.type === "picked" && event.data.did && event.origin === location.origin) {
window.parent.postMessage({ type: "picked", did: event.data.did }, parentOrigin);
return;
}
if (event.origin !== parentOrigin) return;
const type = event.data && event.data.type;
const did = event.data && event.data.did;
if (type === "heartbeat" && did) {
await api("POST", "/xrpc/fyi.loggedin.account.loggedIn", { did, origin: parentOrigin });
const data = await api("GET", "/xrpc/fyi.loggedin.account.list");
window.parent.postMessage({ type: "accounts", accounts: data.accounts }, parentOrigin);
}
if (type === "logout" && did) {
await api("POST", "/xrpc/fyi.loggedin.account.logout", { did, origin: parentOrigin });
const data = await api("GET", "/xrpc/fyi.loggedin.account.list");
window.parent.postMessage({ type: "accounts", accounts: data.accounts }, parentOrigin);
}
}
boot().catch(() => {
if (parentOrigin && isHttpOrigin(parentOrigin)) showChoose();
});
`;
return shell(
"loggedin.fyi embed",
"",
``,
);
}
export function choosePage(parentOrigin: string): string {
const script = `
const PARENT = ${JSON.stringify(parentOrigin)};
async function main() {
const res = await fetch("/xrpc/fyi.loggedin.account.list", { credentials: "include" });
const data = await res.json();
const root = document.getElementById("list");
if (!data.accounts || data.accounts.length === 0) {
root.innerHTML = 'No accounts saved on this device yet.
';
return;
}
const needed = [...new Set(data.accounts.flatMap((a) => a.origins || []))];
const LABELS = await loadOriginLabels(needed);
for (const account of data.accounts) {
const btn = document.createElement("button");
btn.className = "account";
btn.type = "button";
const handle = await resolveHandle(account.did);
const originNames = account.origins.map((o) => LABELS[o] || o).join(", ");
btn.innerHTML = "" + escapeHtml(handle) + " " + escapeHtml(originNames) + " ";
btn.addEventListener("click", () => pick(account.did));
root.appendChild(btn);
}
}
async function loadOriginLabels(origins) {
const labels = {};
if (!origins.length) return labels;
try {
const qs = origins.map((o) => "origin=" + encodeURIComponent(o)).join("&");
const res = await fetch("/origins?" + qs);
const data = await res.json();
for (const row of data.origins || []) labels[row.origin] = row.name;
} catch {}
return labels;
}
function pick(did) {
const payload = { type: "picked", did };
if (window.opener) {
try {
if (window.opener.parent && window.opener.parent !== window.opener) {
window.opener.parent.postMessage(payload, PARENT);
} else {
window.opener.postMessage(payload, PARENT);
}
} catch {
window.opener.postMessage(payload, PARENT);
}
}
window.close();
}
async function resolveHandle(did) {
try {
const res = await fetch("https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=" + encodeURIComponent(did));
if (!res.ok) return did;
const profile = await res.json();
return profile.handle ? "@" + profile.handle : did;
} catch {
return did;
}
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, (c) => ({
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
}[c]));
}
main();
`;
return shell(
"Choose an account — loggedin.fyi",
`
loggedin.fyi
Continue with an account you already use
These DIDs were saved on this device by participating AT Proto apps. Picking one sends it back so the app can start a normal login.
`,
);
}
export function enrollPage(did: string, returnUrl: string): string {
const script = `
const DID = ${JSON.stringify(did)};
const RETURN_URL = ${JSON.stringify(returnUrl)};
document.getElementById("save").addEventListener("click", async () => {
const btn = document.getElementById("save");
btn.disabled = true;
try {
const origin = new URL(RETURN_URL).origin;
const res = await fetch("/xrpc/fyi.loggedin.account.loggedIn", {
method: "POST",
credentials: "include",
headers: { "content-type": "application/json" },
body: JSON.stringify({ did: DID, origin }),
});
if (!res.ok) throw new Error("save failed");
location.href = RETURN_URL;
} catch (err) {
btn.disabled = false;
document.getElementById("err").textContent = String(err.message || err);
}
});
document.getElementById("skip").addEventListener("click", () => {
location.href = RETURN_URL;
});
`;
return shell(
"Save this account — loggedin.fyi",
`
loggedin.fyi
Save this account on this device
Other AT Proto apps can then offer it as a login hint. This does not sign you in anywhere.
${escapeHtml(did)}
Save
Not now
`,
);
}
export function savePage(did: string, origin: string): string {
const script = `
const DID = ${JSON.stringify(did)};
const ORIGIN = ${JSON.stringify(origin)};
async function run() {
const btn = document.getElementById("save");
const err = document.getElementById("err");
try {
const res = await fetch("/xrpc/fyi.loggedin.account.loggedIn", {
method: "POST",
credentials: "include",
headers: { "content-type": "application/json" },
body: JSON.stringify({ did: DID, origin: ORIGIN }),
});
if (!res.ok) throw new Error("save failed");
if (window.opener) {
window.opener.postMessage({ type: "saved", did: DID }, ORIGIN);
}
window.close();
} catch (e) {
btn.disabled = false;
btn.style.display = "";
err.textContent = String(e.message || e);
}
}
document.getElementById("save").addEventListener("click", run);
run();
`;
return shell(
"Saving — loggedin.fyi",
`
loggedin.fyi
Saving account…
${escapeHtml(did)}
Retry
`,
);
}
export function savePageLogout(did: string, origin: string): string {
const script = `
const DID = ${JSON.stringify(did)};
const ORIGIN = ${JSON.stringify(origin)};
async function run() {
try {
const res = await fetch("/xrpc/fyi.loggedin.account.logout", {
method: "POST",
credentials: "include",
headers: { "content-type": "application/json" },
body: JSON.stringify({ did: DID, origin: ORIGIN }),
});
if (!res.ok) throw new Error("logout failed");
if (window.opener) {
window.opener.postMessage({ type: "saved", did: DID, action: "logout" }, ORIGIN);
}
window.close();
} catch (e) {
document.getElementById("err").textContent = String(e.message || e);
}
}
run();
`;
return shell(
"Removing — loggedin.fyi",
`
loggedin.fyi
Removing account…
${escapeHtml(did)}
`,
);
}
export type NativeStartOptions = {
appName: string;
callback: string;
origin: string;
state: string | null;
token: string;
/** Set for the post-login save flow; omitted for the account chooser. */
did?: string;
};
/**
* First-party page opened by a native SDK through the shared browser cookie
* jar (ASWebAuthenticationSession on iOS, a Custom Tab on Android). The cookie
* is attached here, so this page — and only this page — can hand the app a
* token for the same session. The token travels on the affirmative action
* only; "Not now" returns `denied=1`.
*/
export function nativeStartPage(options: NativeStartOptions): string {
const script = `
const CALLBACK = ${jsonForScript(options.callback)};
const ORIGIN = ${jsonForScript(options.origin)};
const STATE = ${jsonForScript(options.state)};
const TOKEN = ${jsonForScript(options.token)};
const SAVE_DID = ${jsonForScript(options.did ?? null)};
function callbackUrl(params) {
const url = new URL(CALLBACK);
if (STATE !== null) url.searchParams.set("state", STATE);
for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);
return url.toString();
}
function finish(params) {
location.href = callbackUrl(params);
}
function deny() {
finish({ denied: "1" });
}
async function api(method, path, body) {
const res = await fetch(path, {
method,
credentials: "include",
headers: body ? { "content-type": "application/json" } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.message || res.statusText);
return data;
}
async function save() {
const btn = document.getElementById("primary");
btn.disabled = true;
try {
await api("POST", "/xrpc/fyi.loggedin.account.loggedIn", { did: SAVE_DID, origin: ORIGIN });
finish({ did: SAVE_DID, token: TOKEN });
} catch (err) {
btn.disabled = false;
document.getElementById("err").textContent = String(err.message || err);
}
}
async function chooser() {
const list = document.getElementById("list");
try {
const data = await api("GET", "/xrpc/fyi.loggedin.account.list");
const accounts = data.accounts || [];
if (accounts.length === 0) {
list.innerHTML = 'No accounts saved on this device yet. Continue to sign in normally.
';
document.getElementById("continue").hidden = false;
return;
}
list.innerHTML = "";
for (const account of accounts) {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "account";
btn.innerHTML =
"" + escapeHtml(account.did) + " " +
"" + escapeHtml((account.origins || []).join(", ")) + " ";
btn.addEventListener("click", () => finish({ did: account.did, token: TOKEN }));
list.appendChild(btn);
}
document.getElementById("continue").hidden = false;
} catch (err) {
list.innerHTML = '' + escapeHtml(String(err.message || err)) + "
";
document.getElementById("continue").hidden = false;
}
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, (c) => ({
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
}[c]));
}
document.getElementById("deny").addEventListener("click", deny);
if (SAVE_DID) {
document.getElementById("primary").addEventListener("click", save);
} else {
document.getElementById("continue").addEventListener("click", () => finish({ token: TOKEN }));
chooser();
}
`;
const body = options.did
? `Remember this account on this device?
${escapeHtml(options.appName)} can let other AT Proto apps on this device offer this account as a login hint. It does not sign you in anywhere else.
${escapeHtml(options.did)}
Remember
Not now
`
: `Continue with an account you already use
These accounts were saved on this device by participating AT Proto apps. Picking one sends it to ${escapeHtml(options.appName)} so it can start a normal login.
Use a different account
Not now
`;
return shell(
"loggedin.fyi",
`
loggedin.fyi
${body}
`,
);
}
export function exampleRendererJs(): string {
return `(() => {
const BSKY = "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile";
function ensureAccountStyles() {
if (document.getElementById("loggedin-account-styles")) return;
const style = document.createElement("style");
style.id = "loggedin-account-styles";
style.textContent = [
".loggedin-account { display: flex; align-items: center; gap: 0.75rem; text-align: left; width: 100%; box-sizing: border-box; cursor: pointer; }",
".loggedin-account-avatar { width: 2.5rem; height: 2.5rem; border-radius: 999px; object-fit: cover; flex-shrink: 0; background: currentColor; opacity: 0.18; }",
"img.loggedin-account-avatar { opacity: 1; background: transparent; }",
".loggedin-account-meta { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }",
".loggedin-account-meta strong, .loggedin-account-meta em, .loggedin-account-meta span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
".loggedin-account-meta em { font-style: normal; opacity: 0.85; }",
".loggedin-account-meta span { opacity: 0.7; font-size: 0.85em; }",
].join("\\n");
document.head.appendChild(style);
}
async function resolveHandle(did) {
try {
const res = await fetch(BSKY + "?actor=" + encodeURIComponent(did));
if (!res.ok) return { handle: did, displayName: "", avatar: "" };
const p = await res.json();
return {
handle: p.handle ? "@" + p.handle : did,
displayName: p.displayName || "",
avatar: typeof p.avatar === "string" ? p.avatar : "",
};
} catch {
return { handle: did, displayName: "", avatar: "" };
}
}
async function renderAccountList(el, accounts, options) {
const onPick = options && options.onPick;
const labels = (options && options.originLabels) || {};
ensureAccountStyles();
el.innerHTML = "";
if (!accounts || accounts.length === 0) {
el.textContent = "No hinted accounts on this device.";
return;
}
for (const account of accounts) {
const profile = await resolveHandle(account.did);
const btn = document.createElement("button");
btn.type = "button";
btn.className = "loggedin-account";
const originNames = (account.origins || []).map((o) => labels[o] || o).join(", ");
const avatar = profile.avatar
? ' '
: ' ';
btn.innerHTML = avatar +
'' +
"" + escapeHtml(profile.handle) + " " +
(profile.displayName ? "" + escapeHtml(profile.displayName) + " " : "") +
"" + escapeHtml(originNames) + " " +
" ";
btn.addEventListener("click", () => onPick && onPick(account.did, profile.handle));
el.appendChild(btn);
}
}
let popupMode = false;
function originOf(iframe, fallback) {
try { return new URL(iframe.src).origin; } catch { return fallback || ""; }
}
function prepareEmbed(iframe) {
const current = iframe.getAttribute("allow") || "";
const parts = current.split(";").map((part) => part.trim()).filter(Boolean);
if (!parts.includes("storage-access")) parts.push("storage-access");
iframe.setAttribute("allow", parts.join("; "));
return iframe;
}
function listenToEmbed(iframe, handlers) {
prepareEmbed(iframe);
window.addEventListener("message", (event) => {
if (event.source === iframe.contentWindow) {
const type = event.data && event.data.type;
if (type === "accounts" && handlers.onAccounts) handlers.onAccounts(event.data.accounts || [], event.origin);
if (type === "unavailable" && handlers.onUnavailable) handlers.onUnavailable();
if (type === "needs-access" && handlers.onNeedsAccess) handlers.onNeedsAccess();
if (type === "popup") {
popupMode = true;
if (handlers.onPopup) handlers.onPopup();
else if (handlers.onUnavailable) handlers.onUnavailable();
}
if (type === "ready" && handlers.onReady) handlers.onReady();
if (type === "picked" && handlers.onPicked) handlers.onPicked(event.data.did);
return;
}
const loggedinOrigin = originOf(iframe, "");
if (loggedinOrigin && event.origin === loggedinOrigin && event.data && event.data.type === "saved" && event.data.did) {
if (handlers.onSaved) handlers.onSaved(event.data.did, event.data.action || "save");
}
});
}
function mountEmbed(iframe, src, handlers) {
prepareEmbed(iframe);
listenToEmbed(iframe, handlers);
iframe.src = src;
}
function listenForPicked(loggedinOrigin, onPicked) {
window.addEventListener("message", (event) => {
if (event.origin !== loggedinOrigin) return;
if (event.data && event.data.type === "picked" && event.data.did) onPicked(event.data.did);
});
}
function saveUrl(loggedinOrigin, did, origin, action) {
const url = new URL("/save", loggedinOrigin);
url.searchParams.set("did", did);
url.searchParams.set("origin", origin);
if (action === "logout") url.searchParams.set("action", "logout");
return url.toString();
}
function openSavePopup(iframe, did, targetOrigin, action) {
const loggedinOrigin = originOf(iframe, targetOrigin);
window.open(
saveUrl(loggedinOrigin, did, location.origin, action),
action === "logout" ? "loggedin-logout" : "loggedin-save",
"width=440,height=600"
);
}
function heartbeat(iframe, did, targetOrigin) {
if (popupMode) {
openSavePopup(iframe, did, targetOrigin, "save");
return;
}
iframe.contentWindow.postMessage({ type: "heartbeat", did }, targetOrigin);
}
function logout(iframe, did, targetOrigin) {
if (popupMode) {
openSavePopup(iframe, did, targetOrigin, "logout");
return;
}
iframe.contentWindow.postMessage({ type: "logout", did }, targetOrigin);
}
function enrollUrl(loggedinOrigin, did, returnUrl) {
const url = new URL("/enroll", loggedinOrigin);
url.searchParams.set("did", did);
url.searchParams.set("return", returnUrl);
return url.toString();
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, (c) => ({
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
}[c]));
}
window.Loggedin = {
renderAccountList,
prepareEmbed,
listenToEmbed,
mountEmbed,
listenForPicked,
heartbeat,
logout,
enrollUrl,
saveUrl,
};
})();
`;
}
function escapeHtml(value: string): string {
return value
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """);
}