|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<meta charset="utf-8" /> |
|
|
<meta name="viewport" content="width=device-width" /> |
|
|
<title>OAuth in a static Space (Vanilla JS)</title> |
|
|
<link rel="stylesheet" href="style.css" /> |
|
|
<style> |
|
|
body { font-family: sans-serif; background: white; color: #222; } |
|
|
.card { max-width: 420px; margin: 2rem auto 0 auto; background: #fff; border-radius: 16px; box-shadow: 0 2px 8px #0001; padding: 2rem; } |
|
|
#status { margin-top: 1rem; word-break: break-all; color: #b00; } |
|
|
button, img { margin-top: 1rem; } |
|
|
pre { background: #eee; padding: 1em; border-radius: 4px; margin-top: 1rem; } |
|
|
#signout { margin-left: 0; } |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="card"> |
|
|
<h1>OAuth in a static Space (Vanilla JS)</h1> |
|
|
<p> |
|
|
This is a demonstration of the Hugging Face OAuth flow in a <b>static Space</b> using only vanilla JS.<br> |
|
|
No external libraries are needed – just copy this HTML file in your Space! |
|
|
|
|
|
After clicking "Signin with HF", you will be redirected to this space and the access token + user info will be displayed. |
|
|
</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="cursor:pointer;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> |
|
|
|
|
|
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'; |
|
|
|
|
|
|
|
|
function showLoggedIn(userinfo) { |
|
|
document.getElementById('signin').style.display = 'none'; |
|
|
document.getElementById('signout').style.display = ''; |
|
|
document.getElementById('status').textContent = 'Logged in!'; |
|
|
document.getElementById('userinfo').style.display = ''; |
|
|
document.getElementById('userinfo').textContent = userinfo; |
|
|
} |
|
|
function showLoggedOut() { |
|
|
document.getElementById('signin').style.display = ''; |
|
|
document.getElementById('signout').style.display = 'none'; |
|
|
document.getElementById('status').textContent = ''; |
|
|
document.getElementById('userinfo').style.display = 'none'; |
|
|
document.getElementById('userinfo').textContent = ''; |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('signin').onclick = function () { |
|
|
const state = Math.random().toString(36).slice(2); |
|
|
localStorage.setItem('hf_oauth_state', state); |
|
|
const url = `${HF_OAUTH_URL}?client_id=${CLIENT_ID}` + |
|
|
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` + |
|
|
`&response_type=code&scope=openid%20profile&state=${state}&prompt=consent`; |
|
|
window.location = url; |
|
|
}; |
|
|
|
|
|
|
|
|
document.getElementById('signout').onclick = function () { |
|
|
localStorage.removeItem('hf_oauth_token'); |
|
|
localStorage.removeItem('hf_oauth_userinfo'); |
|
|
localStorage.removeItem('hf_oauth_state'); |
|
|
showLoggedOut(); |
|
|
window.history.replaceState({}, '', window.location.pathname); |
|
|
}; |
|
|
|
|
|
|
|
|
window.onload = async function () { |
|
|
|
|
|
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 = 'Invalid state, possible CSRF detected.'; |
|
|
return; |
|
|
} |
|
|
const code = params.get('code'); |
|
|
const body = new URLSearchParams({ |
|
|
client_id: CLIENT_ID, |
|
|
grant_type: 'authorization_code', |
|
|
code: code, |
|
|
redirect_uri: REDIRECT_URI |
|
|
}); |
|
|
document.getElementById('status').textContent = 'Exchanging code for token...'; |
|
|
const resp = await fetch(HF_TOKEN_URL, { |
|
|
method: 'POST', |
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
|
|
body |
|
|
}); |
|
|
const data = await resp.json(); |
|
|
if (data.access_token) { |
|
|
localStorage.setItem('hf_oauth_token', data.access_token); |
|
|
|
|
|
const respUser = await fetch('https://huggingface.co/oauth/userinfo', { |
|
|
headers: { Authorization: `Bearer ${data.access_token}` } |
|
|
}); |
|
|
const userinfo = await respUser.json(); |
|
|
const userinfoStr = JSON.stringify(userinfo, null, 2); |
|
|
localStorage.setItem('hf_oauth_userinfo', userinfoStr); |
|
|
showLoggedIn(userinfoStr); |
|
|
|
|
|
window.history.replaceState({}, '', window.location.pathname); |
|
|
} else { |
|
|
document.getElementById('status').textContent = 'OAuth failed: ' + JSON.stringify(data); |
|
|
showLoggedOut(); |
|
|
} |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const token = localStorage.getItem('hf_oauth_token'); |
|
|
const userinfo = localStorage.getItem('hf_oauth_userinfo'); |
|
|
if (token && userinfo) { |
|
|
showLoggedIn(userinfo); |
|
|
} else { |
|
|
showLoggedOut(); |
|
|
} |
|
|
}; |
|
|
</script> |
|
|
</body> |
|
|
</html> |