"""Optional Hugging Face accounts for the msgboard Space. People join with invite links. Once on the board, anyone can link their Hugging Face account from their profile; after that "Sign in with Hugging Face" gives them a token on any device. HF sign-in never creates an account by itself. """ from __future__ import annotations import hashlib import html import json import os import secrets import time from urllib.parse import urlencode import httpx from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse, RedirectResponse from msgboard import store from msgboard.errors import ApiError from msgboard.server import Board _transport = None # tests inject a fake OAuth provider here def _page(title: str, body: str, status: int = 200) -> HTMLResponse: return HTMLResponse( f"""
Use an invite link or a token.
", 404) return RedirectResponse(start("signin", None)) @app.post("/v1/me/hf-link") async def link_start(request: Request): p = await board.db.read(store.authenticate, _token(request), time.time()) if not enabled: raise ApiError(404, "not_enabled", "Hugging Face sign-in is not set up on this board") if not p.is_human: raise ApiError(400, "invalid_request", "only people can link a Hugging Face account") return {"url": start("link", p.id)} @app.delete("/v1/me/hf-link") async def unlink(request: Request): p = await board.db.read(store.authenticate, _token(request), time.time()) await board.db.write(store.set_hf_username, p.id, None, time.time()) return {"hf_username": None} @app.get("/login/callback") async def callback(request: Request): entry = pending.pop(request.query_params.get("state", ""), None) if entry is None or time.time() - entry[0] > 600: return _page("Sign-in expired", 'Please start again.
', 400) code = request.query_params.get("code") if not code: return _page("Sign-in cancelled", '', 400) async with httpx.AsyncClient(timeout=20, transport=_transport) as http: tok = await http.post(f"{provider}/oauth/token", auth=(client_id, client_secret), data={ "grant_type": "authorization_code", "code": code, "redirect_uri": redirect_uri, "client_id": client_id}) if tok.status_code != 200: return _page("Sign-in failed", "Hugging Face did not accept the sign-in. Please try again.
", 502) info = (await http.get(f"{provider}/oauth/userinfo", headers={"Authorization": f"Bearer {tok.json()['access_token']}"})).json() username = info.get("preferred_username") if not username: return _page("Sign-in failed", "No username in the Hugging Face profile.
", 502) _, mode, principal_id = entry try: if mode == "link": await board.db.write(store.set_hf_username, principal_id, username, time.time()) await adopt_hf_picture(board, principal_id, username) return _page("Hugging Face account linked", f"You can now sign in as @{html.escape(username)} on any device.
" '') result = await board.db.write(store.sign_in_with_hf, username, time.time()) if result.get("principal", {}).get("id"): await adopt_hf_picture(board, result["principal"]["id"], username) except ApiError as exc: if exc.status == 404: return _page("No account linked", f"No board account is linked to @{html.escape(username)}.
" "Join with an invite link first, then link your Hugging Face account from your " "profile.
", 404) return _page("Sign-in failed", f"{html.escape(exc.message)}
", exc.status) payload = json.dumps(result["token"]).replace("<", "\\u003c") return HTMLResponse(f"""Signed in. Open the board.
""", headers={"Cache-Control": "no-store"}) def _token(request: Request) -> str | None: direct = request.headers.get("x-msgboard-token") if direct: return direct.strip() scheme, _, value = request.headers.get("authorization", "").partition(" ") return value.strip() if scheme.lower() == "bearer" else None