""" HuggingFace OAuth authentication service. Feature: 012-profile-contact-ui """ import os from authlib.integrations.flask_client import OAuth class AuthService: """HuggingFace OAuth service.""" def __init__(self, app=None): self.oauth = OAuth() self.hf = None if app: self.init_app(app) def init_app(self, app): """Initialize OAuth with Flask app.""" self.oauth.init_app(app) # Get OAuth URLs from environment (allows mock OAuth for local dev) authorization_url = os.getenv( "HF_AUTHORIZATION_URL", "https://huggingface.co/oauth/authorize" ) token_url = os.getenv( "HF_TOKEN_URL", "https://huggingface.co/oauth/token" ) userinfo_url = os.getenv( "HF_USERINFO_URL", "https://huggingface.co/oauth/userinfo" ) jwks_uri = os.getenv( "HF_JWKS_URI", "https://huggingface.co/oauth/jwks" ) # Register OAuth provider (HuggingFace or mock) # HF Spaces provides OAUTH_CLIENT_ID/SECRET when hf_oauth: true # Local dev uses HF_CLIENT_ID/SECRET client_id = os.getenv("OAUTH_CLIENT_ID") or os.getenv("HF_CLIENT_ID") client_secret = os.getenv("OAUTH_CLIENT_SECRET") or os.getenv("HF_CLIENT_SECRET") self.hf = self.oauth.register( name="huggingface", client_id=client_id, client_secret=client_secret, authorize_url=authorization_url, access_token_url=token_url, userinfo_endpoint=userinfo_url, jwks_uri=jwks_uri, client_kwargs={"scope": "openid profile email"}, # Set update_token to None to avoid unnecessary token updates update_token=None, ) def get_authorization_url(self, redirect_uri: str) -> str: """ Get HuggingFace OAuth authorization URL. Args: redirect_uri: Callback URL for OAuth flow Returns: Authorization URL string """ if not self.hf: raise RuntimeError("OAuth not initialized. Call init_app() first.") return self.hf.authorize_redirect(redirect_uri) def fetch_token(self, **kwargs): """ Exchange authorization code for access token. Returns: Token dict with access_token, refresh_token, etc. """ if not self.hf: raise RuntimeError("OAuth not initialized. Call init_app() first.") return self.hf.authorize_access_token(**kwargs) def fetch_userinfo(self, token): """ Fetch user information using access token. Args: token: Access token dict Returns: User info dict with sub, name, preferred_username, picture, email """ if not self.hf: raise RuntimeError("OAuth not initialized. Call init_app() first.") return self.hf.userinfo(token=token) # Global auth service instance auth_service = AuthService()