File size: 5,992 Bytes
22665fb 3f84555 22665fb f91a47c 69b83cc 22665fb b3e7f02 22665fb 69b83cc b3e7f02 69b83cc b3e7f02 69b83cc ea67b4c b3e7f02 283947d f91a47c 69b83cc 22665fb b3e7f02 9b42726 b3e7f02 f91a47c b3e7f02 f91a47c b3e7f02 f91a47c b3e7f02 f91a47c b3e7f02 8ddcc75 b3e7f02 8ddcc75 b3e7f02 8ddcc75 b3e7f02 8ddcc75 b3e7f02 ea67b4c 8ddcc75 ea67b4c 8ddcc75 b3e7f02 22665fb b3e7f02 9b42726 22665fb b3e7f02 ea67b4c b3e7f02 ea67b4c b3e7f02 ea67b4c d18f624 b3e7f02 8ddcc75 b3e7f02 ea67b4c b3e7f02 ea67b4c b3e7f02 69b83cc b3e7f02 69b83cc b3e7f02 ea67b4c 22665fb ea67b4c b3e7f02 ea67b4c b3e7f02 22665fb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
<!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> |