/* eslint-disable @typescript-eslint/no-confusing-void-expression */ /* eslint-disable @typescript-eslint/await-thenable */ import { getOAuthConfig } from '@/constants/oauthConfig'; import { SupportedStorageServices } from '@services/types'; import { useCallback, useEffect } from 'react'; export function useAuth(storageService: SupportedStorageServices): [() => Promise, () => Promise] { const onClickLogout = useCallback(async () => { try { // Clear tokens await window.service.auth.set(`${storageService}-token`, ''); await window.service.auth.set(`${storageService}-userName`, ''); await window.service.auth.set(`${storageService}-email`, ''); // Clear browser cookies for specific OAuth provider domain // This clears the "remember me" state for only this service const config = getOAuthConfig(storageService); if (config?.authorizePath) { try { const oauthUrl = new URL(config.authorizePath); const domain = oauthUrl.hostname; // Clear cookies for this specific domain only await window.service.auth.clearCookiesForDomain(domain); } catch (error) { void window.service.native.log('error', 'Failed to parse OAuth URL or clear cookies', { function: 'useAuth', error }); } } void window.service.native.log('info', 'Logged out from service', { function: 'useAuth', storageService }); } catch (error) { void window.service.native.log('error', 'TokenForm: auth operation failed', { function: 'useAuth', error }); } }, [storageService]); const onClickLogin = useCallback(async () => { await onClickLogout(); try { void window.service.native.log('info', 'Initiating OAuth login', { function: 'useAuth', storageService, }); // Let main process handle everything (OAuth URL generation, PKCE, state management) // using oidc-client-ts internally await window.service.auth.openOAuthWindow(storageService); } catch (error) { console.error(error); void window.service.native.log('error', 'Failed to open OAuth login window', { function: 'useAuth', error }); } }, [onClickLogout, storageService]); return [onClickLogin, onClickLogout]; } export function useGetGithubUserInfoOnLoad(): void { useEffect(() => { void window.service.native.log('debug', 'useGetGithubUserInfoOnLoad: hook mounted', { function: 'useGetGithubUserInfoOnLoad' }); void Promise.all([window.service.auth.get('userName'), window.service.auth.getUserInfos()]).then(async ([userName, userInfo]) => { try { const token = userInfo[`${SupportedStorageServices.github}-token`]; void window.service.native.log('debug', 'useGetGithubUserInfoOnLoad: checking token', { function: 'useGetGithubUserInfoOnLoad', hasToken: !!token, tokenLength: token?.length ?? 0, }); if (token) { const config = getOAuthConfig(SupportedStorageServices.github); if (!config) return; // get user name and email using OAuth provider's API const response = await fetch(config.userInfoPath, { method: 'GET', headers: { Authorization: `Bearer ${token}`, }, }); const gitUserInfo = await (response.json() as Promise<{ email: string; login: string; name: string }>); // this hook will execute on every open of GitTokenForm, so we need to check if we already have the info, not overwrite userInfo that user manually written. if (!userInfo[`${SupportedStorageServices.github}-userName`] && gitUserInfo.login) { userInfo[`${SupportedStorageServices.github}-userName`] = gitUserInfo.login; } if (!userInfo[`${SupportedStorageServices.github}-email`] && gitUserInfo.email) { userInfo[`${SupportedStorageServices.github}-email`] = gitUserInfo.email; } // sometimes user already pick a Chinese username that is different from the one on Git service if (!userName && gitUserInfo.name) { userInfo.userName = gitUserInfo.name; } await window.service.auth.setUserInfos(userInfo); } } catch (error) { void window.service.native.log('error', 'TokenForm: get github user info failed', { function: 'useGetGithubUserInfoOnLoad', error }); } }); }, []); }