| 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"; |
| |
| |
| |
| |
| 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 { |
| |
| } |
| } |
|
|
| 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 { |
| |
| } |
| } |
|
|
| 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 { |
| |
| } |
| } |
|
|
| 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 }); |
| |
| |
| 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(); |
| |
| |
| |
| |
| 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; |
| |
| |
| 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(); |
|
|