Merge pull request #35 from rkheftan/feat/google-auth

feat: google authentication added
This commit is contained in:
SajadMRjl
2025-09-24 11:21:59 +03:30
committed by GitHub
5 changed files with 121 additions and 8 deletions

2
.env
View File

@@ -1,4 +1,4 @@
VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com
VITE_GOOGLE_CLIENT_ID=272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com
VITE_DEFUALT_AUTH_RETURN_URL=/setting/profile
VITE_APP_URL=https://accounts.business-harmony.com
VITE_API_URL=https://accounts.business-harmony.com/api

View File

@@ -21,6 +21,7 @@
} catch (e) {}
})();
</script>
<script src="https://accounts.google.com/gsi/client" async defer></script>
</head>
<body>

View File

@@ -25,6 +25,9 @@ export interface GoogleAuthenticationProps {
) => void;
}
/**
* @deprecated use V2 instead
*/
export const GoogleAuthentication = ({
disabled,
authFactory,
@@ -44,11 +47,8 @@ export const GoogleAuthentication = ({
document.body.appendChild(script);
script.onload = () => {
clientRef.current = google.accounts.oauth2.initCodeClient({
clientRef.current = google.accounts.id.initialize({
client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID,
scope: 'openid email profile',
ux_mode: 'popup',
response_type: 'code',
callback: async (resp: GoogleCodeClientResponse) => {
const apiRequest: LoginOrSignUpWithGoogleRequest = {
idToken: resp.id_token,
@@ -88,6 +88,7 @@ export const GoogleAuthentication = ({
return (
<Button
type="button"
onClick={handleGoogleLogin}
disabled={disabled}
loading={loginWithGoogleLoading}

View File

@@ -0,0 +1,112 @@
import { useEffect, useRef } from 'react';
import type {
LoginOrSignUpWithGoogleRequest,
LoginResult,
} from '../../types/userTypes';
import type { AuthFactory } from '../../types/authTypes';
import {
generateTokenWithGoogle,
type GenerateTokenResponse,
} from '../../api/identityAPI';
import { loginOrSignUpWithGoogle } from '../../api/authorizationAPI';
import { useApi } from '@/hooks/useApi';
import { Icon, useToast } from '@rkheftan/harmony-ui';
import { useTranslation } from 'react-i18next';
import { Box, Button } from '@mui/material';
import { Google } from 'iconsax-react';
interface GoogleAuthenticationV2Props {
disabled: boolean;
authFactory: AuthFactory;
onGoogleAuthenticated: (
loginResult: LoginResult,
tokenResponse: GenerateTokenResponse,
) => void;
}
export const GoogleAuthenticationV2 = ({
disabled,
authFactory,
onGoogleAuthenticated,
}: GoogleAuthenticationV2Props) => {
const toast = useToast();
const { t } = useTranslation('authentication');
const { loading: loginWithGoogleLoading, execute: loginWithGoogleCall } =
useApi(loginOrSignUpWithGoogle);
const googleButtonRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const initializeData = {
client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID,
callback: handleCredentialResponse,
};
google.accounts.id.initialize(initializeData);
google.accounts.id.renderButton(
document.getElementById('google-signin-button'),
{ theme: 'outline' },
);
}, []);
const handleCredentialResponse = async (response: { credential: any }) => {
const idToken = response.credential;
try {
const apiRequest: LoginOrSignUpWithGoogleRequest = {
idToken: idToken,
returnUrl: authFactory.redirectUrl,
};
const res = await loginWithGoogleCall(apiRequest);
if (!res) return;
if (res.success) {
const tokenRes = await generateTokenWithGoogle({
...apiRequest,
client_id: authFactory.clientId,
});
onGoogleAuthenticated(res, tokenRes.data);
} else {
toast({
message: t('loginForm.googleAuthenticationFailed'),
severity: 'error',
});
}
} catch (error) {
toast({
message: t('loginForm.googleAuthenticationFailed'),
severity: 'error',
});
}
};
const handleGoogleLogin = () => {
if (googleButtonRef.current) {
const googleCustomRenderedDivs =
googleButtonRef.current.querySelectorAll('div');
googleCustomRenderedDivs.forEach((b) => b.click());
}
};
return (
<>
<Box sx={{ display: 'none !important' }}>
<div ref={googleButtonRef} id="google-signin-button"></div>
</Box>
<Button
type="button"
onClick={handleGoogleLogin}
disabled={disabled}
loading={loginWithGoogleLoading}
variant="outlined"
startIcon={<Icon Component={Google} variant="Bold" />}
>
{t('loginForm.loginWithGoogle')}
</Button>
</>
);
};

View File

@@ -9,12 +9,11 @@ import { CountryCodeSelector } from '../CountryCodeSelector';
import type { LoginResult, UserStatus } from '../../types/userTypes';
import { getUserStatusByPhoneNumberOrEmail } from '../../api/authorizationAPI';
import type { CountryCode } from '@/types/commonTypes';
import { GoogleAuthentication } from './GoogleAuthentication';
import { isPhoneNumber } from '@/utils/regexes/isValidPhoneNumber';
import { useToast } from '@rkheftan/harmony-ui';
import { useApi } from '@/hooks/useApi';
import type { GenerateTokenResponse } from '../../api/identityAPI';
import { replacePersianWithRealNumbers } from '@/utils/replacePersianWithRealNumbers';
import { GoogleAuthenticationV2 } from './GoogleAuthenticationV2';
export interface LoginRegisterFormProps {
loginRegisterValue: string;
@@ -175,7 +174,7 @@ export function LoginRegisterForm({
{t('loginForm.submitButton')}
</Button>
<GoogleAuthentication
<GoogleAuthenticationV2
authFactory={authFactory}
onGoogleAuthenticated={onGoogleAuthenticated}
disabled={userStatusLoading}