|
| 1 | +"""Predefined button to sign in with Hugging Face in a Gradio Space.""" |
| 2 | +from __future__ import annotations |
| 3 | + |
| 4 | +import warnings |
| 5 | +from typing import Any, Literal |
| 6 | + |
| 7 | +from gradio_client.documentation import document, set_documentation_group |
| 8 | + |
| 9 | +from gradio.components import Button |
| 10 | +from gradio.context import Context |
| 11 | +from gradio.routes import Request |
| 12 | + |
| 13 | +set_documentation_group("component") |
| 14 | + |
| 15 | + |
| 16 | +@document() |
| 17 | +class LoginButton(Button): |
| 18 | + """ |
| 19 | + Button that redirects the user to Sign with Hugging Face using OAuth. |
| 20 | + """ |
| 21 | + |
| 22 | + is_template = True |
| 23 | + |
| 24 | + def __init__( |
| 25 | + self, |
| 26 | + *, |
| 27 | + value: str = "Sign in with Hugging Face", |
| 28 | + variant: Literal["primary", "secondary", "stop"] = "secondary", |
| 29 | + size: Literal["sm", "lg"] | None = None, |
| 30 | + icon: str |
| 31 | + | None = "https://huggingface.co/front/assets/huggingface_logo-noborder.svg", |
| 32 | + link: str | None = None, |
| 33 | + visible: bool = True, |
| 34 | + interactive: bool = True, |
| 35 | + elem_id: str | None = None, |
| 36 | + elem_classes: list[str] | str | None = None, |
| 37 | + scale: int | None = 0, |
| 38 | + min_width: int | None = None, |
| 39 | + **kwargs, |
| 40 | + ): |
| 41 | + super().__init__( |
| 42 | + value, |
| 43 | + variant=variant, |
| 44 | + size=size, |
| 45 | + icon=icon, |
| 46 | + link=link, |
| 47 | + visible=visible, |
| 48 | + interactive=interactive, |
| 49 | + elem_id=elem_id, |
| 50 | + elem_classes=elem_classes, |
| 51 | + scale=scale, |
| 52 | + min_width=min_width, |
| 53 | + **kwargs, |
| 54 | + ) |
| 55 | + if Context.root_block is not None: |
| 56 | + self.activate() |
| 57 | + else: |
| 58 | + warnings.warn( |
| 59 | + "LoginButton created outside of a Blocks context. May not work unless you call its `activate()` method manually." |
| 60 | + ) |
| 61 | + |
| 62 | + def activate(self): |
| 63 | + # Taken from https://cmgdo.com/external-link-in-gradio-button/ |
| 64 | + # Taking `self` as input to check if user is logged in |
| 65 | + # ('self' value will be either "Sign in with Hugging Face" or "Signed in as ...") |
| 66 | + self.click(fn=None, inputs=[self], outputs=None, _js=_js_open_if_not_logged_in) |
| 67 | + |
| 68 | + self.attach_load_event(self._check_login_status, None) |
| 69 | + |
| 70 | + def _check_login_status(self, request: Request) -> dict[str, Any]: |
| 71 | + # Each time the page is refreshed or loaded, check if the user is logged in and adapt label |
| 72 | + session = getattr(request, "session", None) or getattr( |
| 73 | + request.request, "session", None |
| 74 | + ) |
| 75 | + if session is None or "oauth_profile" not in session: |
| 76 | + return self.update("Sign in with Hugging Face", interactive=True) |
| 77 | + else: |
| 78 | + username = session["oauth_profile"]["preferred_username"] |
| 79 | + return self.update(f"Signed in as {username}", interactive=False) |
| 80 | + |
| 81 | + |
| 82 | +# JS code to redirects to /login/huggingface if user is not logged in. |
| 83 | +# If the app is opened in an iframe, open the login page in a new tab. |
| 84 | +# Otherwise, redirects locally. Taken from https://stackoverflow.com/a/61596084. |
| 85 | +_js_open_if_not_logged_in = """ |
| 86 | +(buttonValue) => { |
| 87 | + if (!buttonValue.includes("Signed in")) { |
| 88 | + if ( window !== window.parent ) { |
| 89 | + window.open('/login/huggingface', '_blank'); |
| 90 | + } else { |
| 91 | + window.location.assign('/login/huggingface'); |
| 92 | + } |
| 93 | + } |
| 94 | +} |
| 95 | +""" |
0 commit comments