Hi
Tl;Dr: I am a bit of a web dev noob and I am having issues reproducing client side HF OAuth tutorial.
The bigger picture: I am trying to do an app with fastHTML but I need some oauth.
Details: I am trying to reproduce: Client Side Oauth - a Hugging Face Space by huggingfacejs but examining the console I get an error. Culprit? window.huggingface
variable is undefined
. Can’t shake it, no matter what I tried.
What I did locally:
- created a hf git repo
- created a bare minimum app with fastHTML
- configured readme, requirements, dockerfile, docker,
- tested locally, it got built but window.huggingface is undefined. Fair enough, there is nobody to populate it
- The rest of bare minimum functions are ok. (it shows stuff on the webpage)
Pushed to HF:
- build went ok,
- app launched,
- in console,
window.huggingface
variable isundefined
. - When clicking “Sign in with HF”, the console shows an error.
HOWEVER, the Client Side Oauth - a Hugging Face Space by huggingfacejs app has the window.huggingface
happily configured, when examining its browser console.
My local output of the index.html, as it is generated by FastHTML is below:
<html><head>
<title>C code reviewing</title>
<script src="https://unpkg.com/es-module-shims@1.7.0/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"@huggingface/hub": "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.13.0/+esm"
}
}
</script>
</head>
<body>
<div>
<p>Some content</p>
</div>
<pre></pre>
<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;" name="signin">
<button id="signout" style="display: none" name="signout">Sign out</button>
<a href="authorized">Authenticate!</a>
<script type="module">
import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
console.log("huggingface env", window.huggingface);
let oauthResult = localStorage.getItem("oauth");
if (oauthResult) {
try {
oauthResult = JSON.parse(oauthResult);
} catch {
oauthResult = null;
}
}
oauthResult ||= await oauthHandleRedirectIfPresent();
if (oauthResult) {
document.querySelector("pre").textContent = JSON.stringify(oauthResult, null, 2);
localStorage.setItem("oauth", JSON.stringify(oauthResult));
document.getElementById("signout").style.removeProperty("display");
document.getElementById("signout").onclick = async function() {
localStorage.removeItem("oauth");
window.location.href = window.location.href.replace(/\?.*$/, '');
window.location.reload();
}
} else {
document.getElementById("signin").style.removeProperty("display");
document.getElementById("signin").onclick = async function() {
// prompt=consent to re-trigger the consent screen instead of silently redirecting
window.location.href = (await oauthLoginUrl({scopes: window.huggingface.variables.OAUTH_SCOPES})) + "&prompt=consent";
}
}
</script>
</body></html>
Infringing line: console.log("huggingface env", window.huggingface);
. It shows undefined
in browser’s console.
Of course, the window.location.href = (await oauthLoginUrl({scopes: window.huggingface.variables.OAUTH_SCOPES})) + "&prompt=consent"
triggers an error in the console:
TypeError: Cannot read properties of undefined (reading 'variables')
at document.getElementById.onclick (?__sign=ey[...]__bGP-Bg:47:77)
Examining the Space html is a bit of a mess, can’t figure out how to separate it from the enclosing iframe so I attached the local output.
For reference, my code:
readme.md:
---
title: C Reviewer
emoji: ⚡
colorFrom: yellow
colorTo: gray
sdk: docker
pinned: false
license: mit
hf_oauth: true
hf_oauth_expiration_minutes: 480
hf_oauth_scopes:
- email
---
Dockerfile:
FROM python:3.11
RUN useradd -m -u 1000 user
WORKDIR /code
COPY --link --chown=1000 . .
RUN mkdir -p /tmp/cache/
RUN chmod a+rwx -R /tmp/cache/
ENV HF_HUB_CACHE=HF_HOME
RUN pip install --no-cache-dir -r requirements.txt
ENV PYTHONUNBUFFERED=1 PORT=7860
CMD ["python", "main.py"]
main.py:
from fasthtml.common import *
app, rt = fast_app()
js_hf_imports = \
"""
{
"imports": {
"@huggingface/hub": "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.13.0/+esm"
}
}
"""
js_block_hf_auth = \
"""
import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
console.log("huggingface env", window.huggingface);
let oauthResult = localStorage.getItem("oauth");
if (oauthResult) {
try {
oauthResult = JSON.parse(oauthResult);
} catch {
oauthResult = null;
}
}
oauthResult ||= await oauthHandleRedirectIfPresent();
if (oauthResult) {
document.querySelector("pre").textContent = JSON.stringify(oauthResult, null, 2);
localStorage.setItem("oauth", JSON.stringify(oauthResult));
document.getElementById("signout").style.removeProperty("display");
document.getElementById("signout").onclick = async function() {
localStorage.removeItem("oauth");
window.location.href = window.location.href.replace(/\?.*$/, '');
window.location.reload();
}
} else {
document.getElementById("signin").style.removeProperty("display");
document.getElementById("signin").onclick = async function() {
// prompt=consent to re-trigger the consent screen instead of silently redirecting
window.location.href = (await oauthLoginUrl({scopes: window.huggingface.variables.OAUTH_SCOPES})) + "&prompt=consent";
}
}
"""
def get_hf_user_data():
pass
@rt('/', methods="get")
def get():
header = Head(Title("C code reviewing"),
Script(src="https://unpkg.com/es-module-shims@1.7.0/dist/es-module-shims.js"),
Script(js_hf_imports, type="importmap"),
)
content = Body(Div(P("Some content")),
Pre(""),
Img(src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-xl-dark.svg",
alt="Sign in with Hugging Face",
style="cursor: pointer; display: none;",
_id = "signin", name=None),
Button("Sign out",_id="signout", style="display: none", name=None),
A("Authenticate!", href="authorized"),
Script(js_block_hf_auth, type="module"))
full_page = Html(header, content)
return full_page
@rt('/change')
def get(): return P('Nice to be here!')
@rt('/authorized', methods="get")
def authorized():
content = Body(P("Do we have authenticated user?"))
return content
serve()
Am I missing something from reproducing the index.html
and I am too tired to see it?
Magic constants injection in window.huggingface
does not work when served from docker?
Cut my losses and start with gradio?
Thank you!
p.s.
As a lean “preacher” I started with the riskiest things first, that is, from all the features that I want my app to have, this appears to be the messiest, even if it is not the first needed.