import { FirebaseApp } from "@firebase/app"; import { useCallback, useEffect, useState } from "react"; import { getAuth, signInWithCustomToken, User as FirebaseUser } from "@firebase/auth"; import { ProjectsApi } from "../api/projects"; import { cacheDelegatedLoginToken, getDelegatedLoginTokenFromCache } from "../utils"; import { ApiError } from "../types"; export type DelegatedLoginProps = { projectsApi: ProjectsApi; firebaseApp?: FirebaseApp, projectId: string, onUserChanged?: (user?: FirebaseUser) => void, onAnalyticsEvent?: (event: string, data?: object) => void; }; export function useDelegatedLogin({ projectsApi, firebaseApp, projectId, onUserChanged, }: DelegatedLoginProps) { const [loginSuccessful, setLoginSuccessful] = useState(false); const [delegatedLoginLoading, setDelegatedLoginLoading] = useState(false); const [delegatedLoginError, setDelegatedLoginError] = useState(undefined); const checkLogin = useCallback(async (skipCache = false) => { console.debug("Checking delegated login", {skipCache, projectId}) if (firebaseApp && projectId) { setDelegatedLoginError(undefined); setDelegatedLoginLoading(true); setLoginSuccessful(false); try { let usedCachedToken = false; let delegatedToken = !skipCache && getDelegatedLoginTokenFromCache(projectId); if (!delegatedToken) { try { console.debug("Delegating login", projectId); delegatedToken = await projectsApi.doDelegatedLogin(projectId); } catch (e) { console.error("Error delegating login", JSON.stringify(e)); setDelegatedLoginError(e as any); } } else { console.debug("Using cached token", projectId); usedCachedToken = true; } if (!delegatedToken) { setDelegatedLoginLoading(false); return; } const auth = getAuth(firebaseApp); signInWithCustomToken(auth, delegatedToken) .then(async (userCredential) => { console.debug("Delegated user signed in", userCredential); onUserChanged?.(userCredential.user); setLoginSuccessful(true); try { cacheDelegatedLoginToken(projectId, delegatedToken); } catch (e) { console.error("Error caching token", e); } }) .catch(async (error) => { if (usedCachedToken) { return await checkLogin(true); } else { console.error("Error signing in with delegated token", error); setLoginSuccessful(false); setDelegatedLoginError(error); } }).finally(() => setDelegatedLoginLoading(false)); } catch (e) { setLoginSuccessful(false); setDelegatedLoginError(e as any); setDelegatedLoginLoading(false); } } }, [firebaseApp, projectId]); useEffect(() => { checkLogin(); }, [checkLogin]); return { loginSuccessful, delegatedLoginLoading, delegatedLoginError, checkLogin }; }