import { UserInfo } from "../types"; import { sha256, randomString } from "./crypto"; const OAUTH_BASE = "https://huggingface.co"; const CONFIG_URL = "/config.json"; export const getSpaceUrl = (): string => { return window.location.origin; }; export const readFragmentParams = () => { const hash = window.location.hash.replace(/^#/, ""); const params = new URLSearchParams(hash); return Object.fromEntries(params.entries()); }; export const getPageSignFromUrl = (): string | null => { try { const params = new URLSearchParams(window.location.search); const sign = params.get("__sign"); return sign && sign.trim().length > 0 ? sign.trim() : null; } catch (_e) { return null; } }; // Configuration loading export const loadConfig = async () => { try { const res = await fetch(CONFIG_URL, { cache: "no-store" }); if (res.ok) return await res.json(); } catch {} return {}; }; export const getClientId = async (): Promise => { const params = new URLSearchParams(window.location.search); const override = params.get("client_id"); if (override && override.trim().length > 0) return override.trim(); const cfg = await loadConfig(); if ( cfg && typeof cfg.client_id === "string" && cfg.client_id.trim().length > 0 ) { return cfg.client_id.trim(); } return "spaces_oauth_client"; }; export const getRedirectUri = async (): Promise => { const cfg = await loadConfig(); if ( cfg && typeof cfg.redirect_uri === "string" && cfg.redirect_uri.trim().length > 0 ) { return cfg.redirect_uri.trim(); } return getSpaceUrl(); }; // OAuth functions export const fetchUserInfo = async (token: string): Promise => { const res = await fetch(`${OAUTH_BASE}/oauth/userinfo`, { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) throw new Error("Failed to fetch user profile"); return res.json(); }; export const startLogin = async () => { const clientId = await getClientId(); const redirectUri = await getRedirectUri(); const scope = "openid profile inference-api"; const state = randomString(16); const codeVerifier = randomString(64); const codeChallenge = await sha256(codeVerifier); sessionStorage.setItem("hf_oauth_state", state); sessionStorage.setItem("hf_code_verifier", codeVerifier); const authUrl = new URL(`${OAUTH_BASE}/oauth/authorize`); authUrl.searchParams.set("client_id", clientId); authUrl.searchParams.set("redirect_uri", redirectUri); authUrl.searchParams.set("response_type", "code"); authUrl.searchParams.set("scope", scope); authUrl.searchParams.set("state", state); authUrl.searchParams.set("code_challenge", codeChallenge); authUrl.searchParams.set("code_challenge_method", "S256"); window.location.assign(authUrl.toString()); }; export const exchangeCodeForToken = async (code: string) => { const clientId = await getClientId(); const redirectUri = await getRedirectUri(); const codeVerifier = sessionStorage.getItem("hf_code_verifier"); if (!codeVerifier) throw new Error("Missing code verifier"); const body = new URLSearchParams({ grant_type: "authorization_code", client_id: clientId, code, redirect_uri: redirectUri, code_verifier: codeVerifier, }); const res = await fetch(`${OAUTH_BASE}/oauth/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body, }); if (!res.ok) throw new Error("Code→token exchange failed"); return res.json(); };