thibaud frere
fix
b3e7f02
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>OAuth in a static Space (Vanilla JS)</title>
<style>
body { font-family: sans-serif; background: white; color: #222; margin: 0; padding: 2rem; }
.card { max-width: 420px; margin: 0 auto; background: #fff; border-radius: 16px; box-shadow: 0 2px 8px #0001; padding: 2rem; border: 1px solid #eee; }
h1 { font-size: 20px; margin-top: 0; }
p { color: #666; font-size: 14px; }
#status { margin-top: 1rem; color: #28a745; }
button, img { margin-top: 1rem; cursor: pointer; }
pre { background: #f8f9fa; padding: 1em; border-radius: 8px; margin-top: 1rem; font-size: 12px; }
</style>
</head>
<body>
<div class="card">
<h1>OAuth in a static Space (Vanilla JS)</h1>
<p>Demonstration of Hugging Face OAuth authentication with PKCE in vanilla JS.</p>
<img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-xl-dark.svg"
alt="Sign in with Hugging Face" id="signin" style="display:none;max-width:100%;">
<button id="signout" style="display:none;">Sign out</button>
<div id="status"></div>
<pre id="userinfo" style="display:none"></pre>
</div>
<script>
// OAuth configuration - automatically injected by HF Spaces
const CLIENT_ID = window.huggingface?.variables?.OAUTH_CLIENT_ID;
const REDIRECT_URI = window.location.origin + window.location.pathname;
const HF_OAUTH_URL = 'https://huggingface.co/oauth/authorize';
const HF_TOKEN_URL = 'https://huggingface.co/oauth/token';
// UI helpers
const showLoggedIn = (userinfo) => {
document.getElementById('signin').style.display = 'none';
document.getElementById('signout').style.display = 'block';
document.getElementById('status').textContent = 'Logged in!';
document.getElementById('userinfo').style.display = 'block';
document.getElementById('userinfo').textContent = userinfo;
};
const showLoggedOut = () => {
document.getElementById('signin').style.display = 'block';
document.getElementById('signout').style.display = 'none';
document.getElementById('status').textContent = '';
document.getElementById('userinfo').style.display = 'none';
};
// PKCE helpers
const generateCodeVerifier = () => {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return btoa(String.fromCharCode(...array)).replace(/[+/=]/g, m => ({'+':'-','/':'_','=':''}[m]));
};
const generateCodeChallenge = async (verifier) => {
const data = new TextEncoder().encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/[+/=]/g, m => ({'+':'-','/':'_','=':''}[m]));
};
// Event handlers
document.getElementById('signin').onclick = async () => {
const state = Math.random().toString(36).slice(2);
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
localStorage.setItem('hf_oauth_state', state);
localStorage.setItem('hf_oauth_code_verifier', codeVerifier);
window.location = `${HF_OAUTH_URL}?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=openid%20profile&state=${state}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
};
document.getElementById('signout').onclick = () => {
localStorage.clear();
showLoggedOut();
window.history.replaceState({}, '', window.location.pathname);
};
// OAuth callback handler
window.onload = async () => {
const params = new URLSearchParams(window.location.search);
if (params.has('code') && params.has('state')) {
const state = params.get('state');
if (state !== localStorage.getItem('hf_oauth_state')) {
document.getElementById('status').textContent = 'Security error detected.';
return;
}
document.getElementById('status').textContent = 'Exchanging code...';
try {
// Exchange code for token
const tokenResp = await fetch(HF_TOKEN_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: params.get('code'),
redirect_uri: REDIRECT_URI,
code_verifier: localStorage.getItem('hf_oauth_code_verifier')
})
});
const { access_token } = await tokenResp.json();
// Fetch user info
const userResp = await fetch('https://huggingface.co/oauth/userinfo', {
headers: { Authorization: `Bearer ${access_token}` }
});
const userinfo = await userResp.json();
// Save and display
const userinfoStr = JSON.stringify(userinfo, null, 2);
localStorage.setItem('hf_oauth_token', access_token);
localStorage.setItem('hf_oauth_userinfo', userinfoStr);
showLoggedIn(userinfoStr);
window.history.replaceState({}, '', window.location.pathname);
} catch (error) {
document.getElementById('status').textContent = `OAuth error: ${error.message}`;
showLoggedOut();
}
return;
}
// Check existing session
const userinfo = localStorage.getItem('hf_oauth_userinfo');
userinfo ? showLoggedIn(userinfo) : showLoggedOut();
};
</script>
</body>
</html>