hfviewer / space-auth.js
hfviewer automation
Sync Space from hfviewer-22514889f757
0ae3824
Raw
History Blame Contribute Delete
21.3 kB
const STORAGE_KEY = "hfviewer:space-native-oauth:v2";
const START_URL = "https://hfviewer.com/api/hf_space_oauth/start";
const CONSUME_URL = "https://hfviewer.com/api/hf_space_oauth/consume";
const DESTINATION_URL = "https://hfviewer.com/api/hf_space_oauth/destination";
const POLL_INTERVAL_MS = 800;
const FLOW_TIMEOUT_MS = 5 * 60 * 1000;
const PREPARE_REFRESH_MS = 4 * 60 * 1000;
const AUTH_CHANGED_EVENT = "hfviewer-space-auth-changed";
// The Space shell intercepts canonical hfviewer.com links during capture and
// treats every root-path URL as "return to the Space landing page", even when
// its query opens an account view. Use the canonicalizing www host so these
// remain ordinary external links; its 301 preserves the full query string.
const ACCOUNT_DESTINATION_ORIGIN = "https://www.hfviewer.com";
const authHost = document.getElementById("hfnx-auth");
const compactAuthHost = document.getElementById("sx-auth");
let oauthResult = readStoredResult();
let rendering = false;
let activeFlow = null;
let preparedFlow = null;
let preparePromise = null;
let accountDestinationLinks = [];
let lastAnnouncedAuthIdentity = null;
let nativeLoginPopover = null;
function readStoredResult() {
try {
const value = localStorage.getItem(STORAGE_KEY);
const result = value ? JSON.parse(value) : null;
const expiresAt = Number(result?.spaceSession?.expiresAt || 0) * 1000;
if (!result || !expiresAt || expiresAt <= Date.now()) {
localStorage.removeItem(STORAGE_KEY);
return null;
}
return result;
} catch {
return null;
}
}
function writeStoredResult(value) {
try {
if (value) localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
else localStorage.removeItem(STORAGE_KEY);
} catch {
// Keep the current-tab session even when durable storage is unavailable.
}
}
function randomFlowValue() {
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);
return Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
}
function userFromResult(result) {
const raw = result?.userInfo || result?.userinfo || result?.user_info || result?.user || {};
const username = String(
raw.preferred_username || raw.preferredUsername || raw.username || raw.name || ""
).trim();
if (!username) return null;
return {
username,
name: String(raw.name || username).trim(),
picture: String(raw.picture || raw.avatar_url || raw.avatarUrl || "").trim(),
profile: String(raw.profile || `https://huggingface.co/${encodeURIComponent(username)}`).trim(),
subject: String(raw.sub || raw.id || "").trim(),
email: String(raw.email || "").trim().toLowerCase(),
};
}
function trackAuthEvent(eventName, properties = {}) {
try {
const productContext =
window.HFVIEWER_PRODUCT_CONTEXT && typeof window.HFVIEWER_PRODUCT_CONTEXT === "object"
? window.HFVIEWER_PRODUCT_CONTEXT
: {};
window.hfviewerAnalyticsBridge?.capture?.(eventName, {
product_surface: "hf_space",
space_repo: String(productContext.space_repo || "unknown"),
space_revision: String(productContext.space_revision || "unknown"),
auth_provider: "hugging_face",
...properties,
});
} catch {
// Authentication must remain independent of analytics availability.
}
}
function identifyAuthenticatedUser(user, source) {
if (!user?.username) return;
try {
window.hfviewerAnalyticsBridge?.identifyHfSession?.(
{
authenticated: true,
email: user.email || "",
user: {
id: user.subject || "",
username: user.username,
name: user.name || user.username,
email: user.email || "",
},
},
source || "hf_space_native_oauth"
);
} catch {
// Consent-gated identity is best effort only.
}
}
function keepReleasePills() {
if (!authHost) return [];
return Array.from(authHost.children).filter((element) =>
element.classList.contains("hfnx-auth__pill")
);
}
function makeAvatar(user) {
if (user.picture) {
const image = document.createElement("img");
image.className = "hfnx-auth__avatar";
image.src = user.picture;
image.alt = "";
image.referrerPolicy = "no-referrer";
return image;
}
const letter = document.createElement("span");
letter.className = "hfnx-auth__avatar hfnx-auth__avatar--letter";
letter.textContent = user.username.charAt(0).toUpperCase();
return letter;
}
function makeHfviewerAccountLink(label, path, target) {
const link = document.createElement("a");
link.className = "hfnx-auth__item";
link.href = new URL(path, ACCOUNT_DESTINATION_ORIGIN).href;
link.target = "_blank";
link.rel = "noopener";
link.textContent = label;
link.setAttribute("role", "menuitem");
link.dataset.accountDestination = target;
link.dataset.accountDestinationReady = "0";
accountDestinationLinks.push(link);
link.addEventListener("click", () => {
trackAuthEvent("space_hfviewer_account_destination_opened", { target });
// The clicked handoff is single-use. Mint its replacement after the
// browser has captured this anchor's current href for native navigation.
link.dataset.accountDestinationReady = "0";
window.setTimeout(() => { void prepareAccountDestination(link); }, 0);
});
return link;
}
function liveSpaceSession() {
const session = oauthResult?.spaceSession;
const expiresAt = Number(session?.expiresAt || 0) * 1000;
if (!session?.id || !session?.secret || !expiresAt || expiresAt <= Date.now()) {
return null;
}
return session;
}
async function prepareAccountDestination(link) {
const session = liveSpaceSession();
const destination = String(link?.dataset?.accountDestination || "");
if (!session || !destination || link.dataset.accountDestinationPreparing === "1") return;
link.dataset.accountDestinationPreparing = "1";
try {
const result = await postJson(DESTINATION_URL, {
link_session_id: session.id,
link_session_secret: session.secret,
destination,
});
const loginUrl = new URL(String(result?.login_url || ""));
if (
!["hfviewer.com", "www.hfviewer.com"].includes(loginUrl.hostname) ||
loginUrl.protocol !== "https:" ||
loginUrl.pathname !== "/api/hf_space_oauth/enter"
) throw new Error("The account handoff URL is invalid");
link.href = loginUrl.toString();
link.dataset.accountDestinationReady = "1";
} catch (error) {
console.warn("Hugging Face Space account handoff preparation failed", error);
link.dataset.accountDestinationReady = "0";
trackAuthEvent("space_hf_login_failed", {
component: "space_account_destination",
failure_stage: "destination_prepare",
destination,
error_name: String(error?.name || "Error").slice(0, 80),
});
} finally {
link.dataset.accountDestinationPreparing = "0";
}
}
function prepareAccountDestinations() {
if (!liveSpaceSession()) {
if (oauthResult) {
oauthResult = null;
writeStoredResult(null);
renderAuth();
void prepareLogin();
}
return;
}
for (const link of accountDestinationLinks) void prepareAccountDestination(link);
}
function makeLoggedOutControl() {
const link = document.createElement("a");
link.dataset.spaceNativeAuth = "login";
link.className = "space-native-auth-login";
link.textContent = activeFlow ? "Signing in…" : "Log in";
link.title = activeFlow
? "Cancel this sign-in and try again"
: "Sign in with Hugging Face";
link.href = preparedFlow?.loginUrl || "#";
link.target = "_blank";
link.rel = "noopener";
link.setAttribute("aria-disabled", preparedFlow || activeFlow ? "false" : "true");
link.addEventListener("click", (event) => {
if (activeFlow) {
event.preventDefault();
failLogin(new Error("Sign-in cancelled"), "handoff_cancelled");
return;
}
if (!preparedFlow) {
event.preventDefault();
void prepareLogin();
return;
}
startLogin(preparedFlow, link);
});
return link;
}
function closeNativeLoginPopover() {
const popover = nativeLoginPopover;
nativeLoginPopover = null;
if (!popover) return;
window.removeEventListener("resize", popover.reposition);
window.removeEventListener("scroll", popover.reposition, true);
document.removeEventListener("pointerdown", popover.onPointerDown, true);
document.removeEventListener("keydown", popover.onKeyDown, true);
popover.element.remove();
}
function positionNativeLoginPopover() {
const popover = nativeLoginPopover;
if (!popover?.anchor?.isConnected) {
closeNativeLoginPopover();
return;
}
const rect = popover.anchor.getBoundingClientRect();
const width = popover.element.offsetWidth || 320;
const margin = 12;
const left = Math.max(
margin,
Math.min(rect.left + rect.width / 2 - width / 2, window.innerWidth - width - margin)
);
popover.element.style.left = `${Math.round(left + window.scrollX)}px`;
popover.element.style.top = `${Math.round(rect.bottom + window.scrollY + 10)}px`;
}
function requestNativeLogin(
anchor,
{
title = "Sign in with Hugging Face",
description = "Continue to use this feature in the Space.",
intent = "protected_action",
} = {}
) {
if (userFromResult(oauthResult) && liveSpaceSession()) return false;
if (!anchor?.isConnected) return false;
closeNativeLoginPopover();
const element = document.createElement("div");
element.className = "hf-watch-popover hf-login-popover space-native-login-popover";
element.setAttribute("role", "dialog");
element.setAttribute("aria-label", title);
const close = document.createElement("button");
close.type = "button";
close.className = "hf-watch-popover__close";
close.setAttribute("aria-label", "Close");
close.textContent = "×";
const titleElement = document.createElement("p");
titleElement.className = "hf-watch-popover__title";
titleElement.textContent = title;
const descriptionElement = document.createElement("p");
descriptionElement.className = "hf-watch-popover__sub";
descriptionElement.textContent = description;
const continueLink = document.createElement("a");
continueLink.className = "hf-login-popover__btn";
continueLink.target = "_blank";
continueLink.rel = "noopener";
continueLink.setAttribute("aria-disabled", "true");
continueLink.innerHTML =
'<img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="" width="22" height="20">' +
'<span>Preparing sign in…</span>';
element.append(close, titleElement, descriptionElement, continueLink);
document.body.appendChild(element);
const popover = {
element,
anchor,
intent,
continueLink,
reposition: () => positionNativeLoginPopover(),
onPointerDown: (event) => {
if (!element.contains(event.target) && !anchor.contains(event.target)) {
closeNativeLoginPopover();
}
},
onKeyDown: (event) => {
if (event.key === "Escape") closeNativeLoginPopover();
},
};
nativeLoginPopover = popover;
close.addEventListener("click", closeNativeLoginPopover);
continueLink.addEventListener("click", (event) => {
if (!preparedFlow || activeFlow) {
event.preventDefault();
if (!activeFlow) void prepareLogin().then(() => refreshNativeLoginPopover());
return;
}
trackAuthEvent("space_hf_login_continue_clicked", {
component: "space_native_login_popover",
login_intent: intent,
});
startLogin(preparedFlow, continueLink);
});
window.addEventListener("resize", popover.reposition);
window.addEventListener("scroll", popover.reposition, true);
document.addEventListener("pointerdown", popover.onPointerDown, true);
document.addEventListener("keydown", popover.onKeyDown, true);
positionNativeLoginPopover();
trackAuthEvent("space_hf_login_preflight_shown", {
component: "space_native_login_popover",
login_intent: intent,
});
void prepareLogin().then(() => refreshNativeLoginPopover());
return true;
}
function refreshNativeLoginPopover() {
const popover = nativeLoginPopover;
if (!popover) return;
if (preparedFlow?.loginUrl && !activeFlow) {
popover.continueLink.href = preparedFlow.loginUrl;
popover.continueLink.setAttribute("aria-disabled", "false");
const label = popover.continueLink.querySelector("span");
if (label) label.textContent = "Continue with Hugging Face";
}
positionNativeLoginPopover();
}
function makeLoggedInControl(user, { compact = false } = {}) {
const wrap = document.createElement("div");
wrap.dataset.spaceNativeAuth = "profile";
wrap.className = "space-native-auth-wrap";
if (compact) wrap.classList.add("space-native-auth-wrap--compact");
const chip = document.createElement("button");
chip.type = "button";
chip.className = "hfnx-auth__chip";
chip.setAttribute("aria-haspopup", "menu");
chip.setAttribute("aria-expanded", "false");
chip.appendChild(makeAvatar(user));
const label = document.createElement("span");
label.textContent = user.username;
chip.appendChild(label);
const menu = document.createElement("div");
menu.className = "hfnx-auth__menu space-native-auth-menu";
menu.setAttribute("role", "menu");
const myModels = makeHfviewerAccountLink(
"My models",
"/?my_models=1&blogs_with_graphs=1&next_landing=1",
"my_models"
);
const bookmarks = makeHfviewerAccountLink(
"Bookmarks",
"/?bookmarks=1&blogs_with_graphs=1&next_landing=1",
"bookmarks"
);
const releaseWatchlist = makeHfviewerAccountLink(
"Release watchlist",
"/?release_watchlist=1&blogs_with_graphs=1&next_landing=1",
"release_watchlist"
);
const writeArticle = makeHfviewerAccountLink(
"Write article",
"/?article_picker=1&login_intent=interactive_articles&blogs_with_graphs=1&next_landing=1",
"write_article"
);
const logout = document.createElement("button");
logout.type = "button";
logout.className = "hfnx-auth__item";
logout.textContent = "Log out";
logout.setAttribute("role", "menuitem");
logout.addEventListener("click", () => {
trackAuthEvent("space_hf_logout");
oauthResult = null;
writeStoredResult(null);
renderAuth();
// Logged-in page loads intentionally do not prepare an OAuth flow. Once
// the profile is cleared, prepare one immediately so the replacement Log
// in control does not remain disabled until it is clicked or the periodic
// refresh runs.
void prepareLogin();
});
const setOpen = (open) => {
menu.classList.toggle("is-open", open);
chip.setAttribute("aria-expanded", open ? "true" : "false");
};
chip.addEventListener("click", (event) => {
event.stopPropagation();
setOpen(!menu.classList.contains("is-open"));
});
document.addEventListener("click", () => setOpen(false));
menu.append(myModels, bookmarks, releaseWatchlist, writeArticle, logout);
wrap.append(chip, menu);
return wrap;
}
function renderAuth() {
if ((!authHost && !compactAuthHost) || rendering) return;
rendering = true;
accountDestinationLinks = [];
const pills = keepReleasePills();
const user = userFromResult(oauthResult);
if (authHost) {
authHost.replaceChildren(...pills, user ? makeLoggedInControl(user) : makeLoggedOutControl());
}
if (compactAuthHost) {
compactAuthHost.replaceChildren();
compactAuthHost.hidden = !user;
if (user) compactAuthHost.appendChild(makeLoggedInControl(user, { compact: true }));
}
queueMicrotask(() => {
rendering = false;
prepareAccountDestinations();
announceAuthState();
});
}
function applyOauthResult(callbackResult, source) {
oauthResult = callbackResult;
writeStoredResult(callbackResult);
activeFlow = null;
renderAuth();
closeNativeLoginPopover();
const user = userFromResult(callbackResult);
identifyAuthenticatedUser(user, source);
trackAuthEvent("space_hf_login_completed", {
component: "space_nav",
username_present: !!user?.username,
subject_present: !!user?.subject,
email_present: !!user?.email,
});
}
function failLogin(error, failureStage) {
console.warn("Hugging Face Space login failed", error);
activeFlow = null;
renderAuth();
trackAuthEvent("space_hf_login_failed", {
component: "space_nav",
failure_stage: failureStage,
error_name: String(error?.name || "Error").slice(0, 80),
});
void prepareLogin();
}
async function postJson(url, body) {
const response = await fetch(url, {
method: "POST",
mode: "cors",
credentials: "omit",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
});
const payload = await response.json().catch(() => ({}));
if (!response.ok && response.status !== 202) {
throw new Error(payload?.error || `Request failed (${response.status})`);
}
return payload;
}
function announceAuthState() {
const user = userFromResult(oauthResult);
const session = liveSpaceSession();
const identity = user && session ? `${user.subject || user.username}:${session.id}` : "";
if (identity === lastAnnouncedAuthIdentity) return;
lastAnnouncedAuthIdentity = identity;
window.dispatchEvent(
new CustomEvent(AUTH_CHANGED_EVENT, {
detail: {
authenticated: !!identity,
username: user?.username || "",
},
})
);
}
async function postAuthenticatedSpaceJson(url, payload) {
const session = liveSpaceSession();
if (!session) throw new Error("The Space login expired. Please sign in again.");
try {
return await postJson(url, {
...(payload || {}),
link_session_id: session.id,
link_session_secret: session.secret,
});
} catch (error) {
if (/login expired|session expired|invalid|unauthorized/i.test(String(error?.message || ""))) {
oauthResult = null;
writeStoredResult(null);
renderAuth();
void prepareLogin();
}
throw error;
}
}
window.hfviewerSpaceAuth = Object.freeze({
isAuthenticated: () => !!userFromResult(oauthResult) && !!liveSpaceSession(),
postJson: postAuthenticatedSpaceJson,
requestLogin: requestNativeLogin,
});
async function pollForCompletion(flow) {
while (activeFlow === flow && Date.now() - flow.startedAt < FLOW_TIMEOUT_MS) {
try {
const result = await postJson(CONSUME_URL, {
flow_id: flow.id,
flow_secret: flow.secret,
});
if (result?.status === "complete" && userFromResult(result)) {
applyOauthResult(
{ userInfo: result.userInfo, spaceSession: result.spaceSession },
"hf_space_native_oauth_handoff"
);
return;
}
} catch (error) {
failLogin(error, "handoff_consume");
return;
}
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
}
if (activeFlow === flow) failLogin(new Error("OAuth flow timed out"), "handoff_timeout");
}
function startLogin(flow, link) {
flow.startedAt = Date.now();
activeFlow = flow;
preparedFlow = null;
// Keep the clicked anchor alive until the browser has performed its native
// target=_blank navigation. Replacing it synchronously can cancel the popup.
link.textContent = "Signing in…";
link.title = "Cancel this sign-in and try again";
link.setAttribute("aria-disabled", "false");
trackAuthEvent("space_hf_login_clicked", { component: "space_nav" });
void pollForCompletion(flow);
}
async function prepareLogin() {
if (oauthResult || activeFlow) return;
if (
preparedFlow &&
Date.now() - preparedFlow.preparedAt < PREPARE_REFRESH_MS
) return;
if (preparePromise) return preparePromise;
const flow = {
id: randomFlowValue(),
secret: randomFlowValue(),
preparedAt: Date.now(),
startedAt: 0,
loginUrl: "",
};
preparePromise = postJson(START_URL, {
flow_id: flow.id,
flow_secret: flow.secret,
}).then((result) => {
const loginUrl = new URL(String(result?.login_url || ""));
if (
loginUrl.origin !== "https://huggingface.co" ||
loginUrl.pathname !== "/oauth/authorize"
) throw new Error("The OAuth login URL is invalid");
flow.loginUrl = loginUrl.toString();
preparedFlow = flow;
if (!activeFlow) renderAuth();
refreshNativeLoginPopover();
}).catch((error) => {
console.warn("Hugging Face Space login preparation failed", error);
trackAuthEvent("space_hf_login_failed", {
component: "space_nav",
failure_stage: "login_prepare",
error_name: String(error?.name || "Error").slice(0, 80),
});
}).finally(() => {
preparePromise = null;
});
return preparePromise;
}
async function initializeApp() {
renderAuth();
identifyAuthenticatedUser(userFromResult(oauthResult), "hf_space_native_oauth_restored");
await prepareLogin();
}
if (authHost) {
const observer = new MutationObserver(() => {
if (!rendering && !authHost.querySelector("[data-space-native-auth]")) renderAuth();
});
observer.observe(authHost, { childList: true });
}
window.addEventListener("storage", (event) => {
if (event.key !== STORAGE_KEY) return;
oauthResult = readStoredResult();
renderAuth();
});
window.setInterval(() => {
void prepareLogin();
prepareAccountDestinations();
}, PREPARE_REFRESH_MS);
void initializeApp();