File size: 4,185 Bytes
f52d137 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
import { Tooltip } from "@mui/material";
import React, { useCallback, useEffect } from "react";
import { UserInfo } from "../types";
import {
exchangeCodeForToken,
fetchUserInfo,
readFragmentParams,
startLogin,
} from "../utils/oauth";
import { LoginButton } from "./LoginButton";
interface HuggingFaceLoginButtonProps {
userInfo: UserInfo | null;
accessToken: string | null;
loginLabel: string;
isDisabled?: boolean;
onLoginStateChange: (
userInfo: UserInfo | null,
accessToken: string | null,
loginLabel: string,
) => void;
}
export const HuggingFaceLoginButton: React.FC<HuggingFaceLoginButtonProps> = ({
userInfo,
accessToken,
loginLabel,
isDisabled = false,
onLoginStateChange,
}) => {
const isLoggedIn = !!userInfo?.sub;
// Handle OAuth redirect
const handleRedirect = useCallback(async () => {
const params = new URLSearchParams(window.location.search);
const { access_token: fragToken, error: fragErr } = readFragmentParams();
if (fragErr) {
onLoginStateChange(null, null, `Error: ${fragErr}`);
return true;
}
const error = params.get("error");
const errorDescription = params.get("error_description");
if (error) {
onLoginStateChange(
null,
null,
`Error: ${error}${errorDescription ? ` — ${errorDescription}` : ""}`,
);
return true;
}
const returnedState = params.get("state");
const expectedState = sessionStorage.getItem("hf_oauth_state");
if (returnedState && expectedState && returnedState !== expectedState) {
onLoginStateChange(null, null, "Error: invalid state");
return true;
}
// Implicit flow
if (fragToken) {
try {
const info = await fetchUserInfo(fragToken);
const label =
info?.email || info?.name || info?.preferred_username || "User";
onLoginStateChange(info, fragToken, label);
} catch (err) {
console.error(err);
onLoginStateChange(null, fragToken, "Connected");
}
window.history.replaceState({}, "", window.location.pathname);
return true;
}
const code = params.get("code");
if (!code) return false;
try {
const tokenResponse = await exchangeCodeForToken(code);
const token = tokenResponse.access_token;
if (token) {
try {
const info = await fetchUserInfo(token);
const label =
info?.email || info?.name || info?.preferred_username || "User";
onLoginStateChange(info, token, label);
} catch (err) {
console.error(err);
onLoginStateChange(null, token, "Connected");
}
} else {
onLoginStateChange(null, null, "Connected");
}
} catch (e: any) {
console.error(e);
onLoginStateChange(null, null, `Authentication error: ${e.message}`);
} finally {
window.history.replaceState({}, "", window.location.pathname);
}
return true;
}, [onLoginStateChange]);
// Initialize on component mount
useEffect(() => {
const initialize = async () => {
const handled = await handleRedirect();
if (!handled) {
onLoginStateChange(null, null, "Login with Hugging Face");
}
};
initialize();
}, [handleRedirect, onLoginStateChange]);
const handleLoginClick = () => {
onLoginStateChange(userInfo, accessToken, "Redirecting to Hugging Face…");
startLogin().catch((err) => {
console.error(err);
onLoginStateChange(userInfo, accessToken, `Error: ${err.message}`);
});
};
return (
<Tooltip title={isLoggedIn ? "Log out" : ""} placement="right">
<LoginButton
icon={
<img
src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
alt="Hugging Face"
style={{ width: 18, height: 18 }}
/>
}
onClick={
isLoggedIn
? () => onLoginStateChange(null, null, "Login with Hugging Face")
: handleLoginClick
}
isLoggedIn={isLoggedIn}
disabled={isDisabled}
>
{loginLabel}
</LoginButton>
</Tooltip>
);
};
|