Merge pull request #30 from rkheftan/feat/multiple-client-id
Feat/multiple client
This commit is contained in:
3
.env
3
.env
@@ -1,7 +1,8 @@
|
|||||||
VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com
|
VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com
|
||||||
VITE_DEFUALT_AUTH_RETURN_URL=/setting/profile
|
VITE_DEFUALT_AUTH_RETURN_URL=/setting/profile
|
||||||
|
VITE_APP_URL=https://account.business-harmony.com
|
||||||
VITE_API_URL=https://accounts.business-harmony.com/api/
|
VITE_API_URL=https://accounts.business-harmony.com/api/
|
||||||
VITE_IDENTITY_URL=https://accounts.business-harmony.com/connect/token
|
VITE_IDENTITY_URL=https://accounts.business-harmony.com/connect/token
|
||||||
VITE_IDENTITY_CLIENT_ID=harmony_identity
|
VITE_IDENTITY_CLIENT_ID=harmony_identity
|
||||||
VITE_IDENTITY_SCOPE=openid profile offline_access harmony_identity
|
VITE_IDENTITY_SCOPE=openid profile offline_access
|
||||||
IMAGE_BASE_URL=https://accounts.business-harmony.com/uploads/
|
IMAGE_BASE_URL=https://accounts.business-harmony.com/uploads/
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import apiClient from '@/lib/apiClient';
|
import apiClient from '@/lib/apiClient';
|
||||||
|
|
||||||
export interface GenerateTokenWithPassword {
|
export interface GenerateToken {
|
||||||
|
client_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenerateTokenWithPassword extends GenerateToken {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
@@ -18,8 +22,11 @@ export const generateTokenWithPassword = (
|
|||||||
) => {
|
) => {
|
||||||
const body = new URLSearchParams();
|
const body = new URLSearchParams();
|
||||||
body.set('grant_type', 'password');
|
body.set('grant_type', 'password');
|
||||||
body.set('client_id', import.meta.env.VITE_IDENTITY_CLIENT_ID);
|
body.set('client_id', request.client_id);
|
||||||
body.set('scope', import.meta.env.VITE_IDENTITY_SCOPE);
|
body.set(
|
||||||
|
'scope',
|
||||||
|
import.meta.env.VITE_IDENTITY_SCOPE + ' ' + request.client_id,
|
||||||
|
);
|
||||||
body.set('username', request.username);
|
body.set('username', request.username);
|
||||||
body.set('password', request.password);
|
body.set('password', request.password);
|
||||||
|
|
||||||
@@ -34,7 +41,7 @@ export const generateTokenWithPassword = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GenerateTokenWithOTP {
|
export interface GenerateTokenWithOTP extends GenerateToken {
|
||||||
email?: string;
|
email?: string;
|
||||||
phonenumber?: string;
|
phonenumber?: string;
|
||||||
otp: string;
|
otp: string;
|
||||||
@@ -43,8 +50,11 @@ export interface GenerateTokenWithOTP {
|
|||||||
export const generateTokenWithOtp = (request: GenerateTokenWithOTP) => {
|
export const generateTokenWithOtp = (request: GenerateTokenWithOTP) => {
|
||||||
const body = new URLSearchParams();
|
const body = new URLSearchParams();
|
||||||
body.set('grant_type', 'otp');
|
body.set('grant_type', 'otp');
|
||||||
body.set('client_id', import.meta.env.VITE_IDENTITY_CLIENT_ID);
|
body.set('client_id', request.client_id);
|
||||||
body.set('scope', import.meta.env.VITE_IDENTITY_SCOPE);
|
body.set(
|
||||||
|
'scope',
|
||||||
|
import.meta.env.VITE_IDENTITY_SCOPE + ' ' + request.client_id,
|
||||||
|
);
|
||||||
if (request.email) body.set('email', request.email);
|
if (request.email) body.set('email', request.email);
|
||||||
if (request.phonenumber) body.set('phonenumber', request.phonenumber);
|
if (request.phonenumber) body.set('phonenumber', request.phonenumber);
|
||||||
body.set('otp_code', request.otp);
|
body.set('otp_code', request.otp);
|
||||||
@@ -60,15 +70,18 @@ export const generateTokenWithOtp = (request: GenerateTokenWithOTP) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GenerateTokenWithGoogle {
|
export interface GenerateTokenWithGoogle extends GenerateToken {
|
||||||
idToken: string;
|
idToken: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateTokenWithGoogle = (request: GenerateTokenWithGoogle) => {
|
export const generateTokenWithGoogle = (request: GenerateTokenWithGoogle) => {
|
||||||
const body = new URLSearchParams();
|
const body = new URLSearchParams();
|
||||||
body.set('grant_type', 'google');
|
body.set('grant_type', 'google');
|
||||||
body.set('client_id', import.meta.env.VITE_IDENTITY_CLIENT_ID);
|
body.set('client_id', request.client_id);
|
||||||
body.set('scope', import.meta.env.VITE_IDENTITY_SCOPE);
|
body.set(
|
||||||
|
'scope',
|
||||||
|
import.meta.env.VITE_IDENTITY_SCOPE + ' ' + request.client_id,
|
||||||
|
);
|
||||||
body.set('idtoken', request.idToken);
|
body.set('idtoken', request.idToken);
|
||||||
|
|
||||||
return apiClient.post<GenerateTokenResponse>(
|
return apiClient.post<GenerateTokenResponse>(
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { useState, type JSX } from 'react';
|
import { useMemo, useState, type JSX } from 'react';
|
||||||
import { LoginRegisterForm } from './LoginRegiserForm';
|
import { LoginRegisterForm } from './LoginRegiserForm';
|
||||||
import type { AuthMode, AuthStep, AuthType } from '../../types/authTypes';
|
import type {
|
||||||
|
AuthFactory,
|
||||||
|
AuthMode,
|
||||||
|
AuthStep,
|
||||||
|
AuthType,
|
||||||
|
} from '../../types/authTypes';
|
||||||
import { OtpVerifyForm } from './OtpVerifyForm';
|
import { OtpVerifyForm } from './OtpVerifyForm';
|
||||||
import { isNumeric } from '@/utils/regexes/isNumeric';
|
import { isNumeric } from '@/utils/regexes/isNumeric';
|
||||||
import { CompleteSignUp } from './CompleteSignUp';
|
import { CompleteSignUp } from './CompleteSignUp';
|
||||||
@@ -9,17 +14,12 @@ import { UserStatus, type LoginResult } from '../../types/userTypes';
|
|||||||
import type { CountryCode } from '@/types/commonTypes';
|
import type { CountryCode } from '@/types/commonTypes';
|
||||||
import { VerifyPhoneNumber } from './VerifyPhoneNumber';
|
import { VerifyPhoneNumber } from './VerifyPhoneNumber';
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { REFRESH_TOKEN_KEY } from '@/providers/AuthProvider';
|
import type { GenerateTokenResponse } from '../../api/identityAPI';
|
||||||
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
|
|
||||||
export const AuthenticationSteps = (): JSX.Element => {
|
export const AuthenticationSteps = (): JSX.Element => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const authReturnUrl: string | null = searchParams.get('returnUrl');
|
|
||||||
const authReturnUrlWithRefreshToken =
|
|
||||||
authReturnUrl + `?token=${sessionStorage.getItem(REFRESH_TOKEN_KEY)}`;
|
|
||||||
authReturnUrl + `?token=${sessionStorage.getItem(REFRESH_TOKEN_KEY)}`;
|
|
||||||
const authReturnUrlOrDefault: string =
|
|
||||||
authReturnUrl ?? import.meta.env.VITE_DEFUALT_AUTH_RETURN_URL;
|
|
||||||
const [authMode, setAuthMode] = useState<AuthMode>('register');
|
const [authMode, setAuthMode] = useState<AuthMode>('register');
|
||||||
const [authType, setAuthType] = useState<AuthType>('phone');
|
const [authType, setAuthType] = useState<AuthType>('phone');
|
||||||
const [currentStep, setCurrentStep] = useState<AuthStep>('emailOrPhone');
|
const [currentStep, setCurrentStep] = useState<AuthStep>('emailOrPhone');
|
||||||
@@ -29,6 +29,41 @@ export const AuthenticationSteps = (): JSX.Element => {
|
|||||||
useState<CountryCode>('+98');
|
useState<CountryCode>('+98');
|
||||||
const [addedPhoneNumberValue, setAddedPhoneNumberValue] =
|
const [addedPhoneNumberValue, setAddedPhoneNumberValue] =
|
||||||
useState<string>('');
|
useState<string>('');
|
||||||
|
const [memoryTokenRes, setMemoryTokenRes] = useState<GenerateTokenResponse>();
|
||||||
|
const { login } = useAuth();
|
||||||
|
|
||||||
|
const authFactory: AuthFactory = useMemo(() => {
|
||||||
|
const redirectUrl = searchParams.get('redirect_url');
|
||||||
|
const clientId = searchParams.get('client_id');
|
||||||
|
|
||||||
|
if (!clientId) {
|
||||||
|
const defaultFactory: AuthFactory = {
|
||||||
|
clientId: import.meta.env.VITE_IDENTITY_CLIENT_ID,
|
||||||
|
redirectUrl: import.meta.env.VITE_APP_URL,
|
||||||
|
isCurrentApplication: function () {
|
||||||
|
return this.clientId === import.meta.env.VITE_IDENTITY_CLIENT_ID;
|
||||||
|
},
|
||||||
|
getFullRedirectUrl: function (token: string) {
|
||||||
|
return this.redirectUrl + '?token=' + token;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return defaultFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resFactory: AuthFactory = {
|
||||||
|
clientId: clientId,
|
||||||
|
redirectUrl: redirectUrl as string,
|
||||||
|
isCurrentApplication: function () {
|
||||||
|
return this.clientId === import.meta.env.VITE_IDENTITY_CLIENT_ID;
|
||||||
|
},
|
||||||
|
getFullRedirectUrl: function (token: string) {
|
||||||
|
return this.redirectUrl + '?token=' + token;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return resFactory;
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
const handleLoginRegister = (value: string, userStatus: UserStatus) => {
|
const handleLoginRegister = (value: string, userStatus: UserStatus) => {
|
||||||
setAuthType(isNumeric(value) ? 'phone' : 'email');
|
setAuthType(isNumeric(value) ? 'phone' : 'email');
|
||||||
@@ -53,40 +88,54 @@ export const AuthenticationSteps = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUserLoggedIn = (loginResult: LoginResult) => {
|
const handleUserLoggedIn = (
|
||||||
|
loginResult: LoginResult,
|
||||||
|
tokenResponse: GenerateTokenResponse,
|
||||||
|
) => {
|
||||||
|
setMemoryTokenRes(tokenResponse);
|
||||||
|
if (authFactory.isCurrentApplication()) {
|
||||||
|
login(tokenResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (loginResult.registeredWithOutPhoneNumber) {
|
if (loginResult.registeredWithOutPhoneNumber) {
|
||||||
setCurrentStep('addPhoneNumber');
|
setCurrentStep('addPhoneNumber');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loginResult.completedUserInformation) {
|
if (!loginResult.completedUserInformation) {
|
||||||
if (authReturnUrl) {
|
if (!authFactory.isCurrentApplication()) {
|
||||||
navigate(`/signup?returnUrl=${authReturnUrlWithRefreshToken}`);
|
navigate(
|
||||||
|
`/signup?returnUrl=${authFactory.getFullRedirectUrl(tokenResponse.refresh_token)}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
navigate(`/signup`);
|
navigate(`/signup`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectToReturnUrl();
|
redirectToReturnUrl(tokenResponse.refresh_token);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePhoneNumberVerified = () => {
|
const handlePhoneNumberVerified = () => {
|
||||||
if (authReturnUrl) {
|
if (!authFactory.isCurrentApplication()) {
|
||||||
navigate(`/signup?returnUrl=${authReturnUrlWithRefreshToken}`);
|
navigate(
|
||||||
|
`/signup?returnUrl=${authFactory.getFullRedirectUrl(memoryTokenRes?.refresh_token as string)}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
navigate(`/signup`);
|
navigate(`/signup`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const redirectToReturnUrl = () => {
|
const redirectToReturnUrl = (refreshToken: string) => {
|
||||||
if (!authReturnUrl) {
|
if (authFactory.isCurrentApplication()) {
|
||||||
navigate(import.meta.env.VITE_DEFUALT_AUTH_RETURN_URL);
|
navigate(import.meta.env.VITE_DEFUALT_AUTH_RETURN_URL);
|
||||||
} else {
|
} else {
|
||||||
if (authMode === 'register') {
|
if (authMode === 'register') {
|
||||||
navigate(`/account-created?returnUrl=${authReturnUrlWithRefreshToken}`);
|
navigate(
|
||||||
|
`/account-created?returnUrl=${authFactory.getFullRedirectUrl(refreshToken)}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
location.href = authReturnUrlWithRefreshToken;
|
location.href = authFactory.getFullRedirectUrl(refreshToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -95,7 +144,7 @@ export const AuthenticationSteps = (): JSX.Element => {
|
|||||||
<>
|
<>
|
||||||
{currentStep === 'emailOrPhone' && (
|
{currentStep === 'emailOrPhone' && (
|
||||||
<LoginRegisterForm
|
<LoginRegisterForm
|
||||||
authReturnUrl={authReturnUrlOrDefault}
|
authFactory={authFactory}
|
||||||
onGoogleAuthenticated={handleUserLoggedIn}
|
onGoogleAuthenticated={handleUserLoggedIn}
|
||||||
countryCode={countryCode}
|
countryCode={countryCode}
|
||||||
setCountryCode={setCountryCode}
|
setCountryCode={setCountryCode}
|
||||||
@@ -109,7 +158,7 @@ export const AuthenticationSteps = (): JSX.Element => {
|
|||||||
|
|
||||||
{currentStep === 'verify' && (
|
{currentStep === 'verify' && (
|
||||||
<OtpVerifyForm
|
<OtpVerifyForm
|
||||||
authReturnUrl={authReturnUrlOrDefault}
|
authFactory={authFactory}
|
||||||
countryCode={countryCode}
|
countryCode={countryCode}
|
||||||
onOTPVerified={handleUserLoggedIn}
|
onOTPVerified={handleUserLoggedIn}
|
||||||
onEditValue={() => setCurrentStep('emailOrPhone')}
|
onEditValue={() => setCurrentStep('emailOrPhone')}
|
||||||
@@ -121,7 +170,7 @@ export const AuthenticationSteps = (): JSX.Element => {
|
|||||||
|
|
||||||
{currentStep === 'enterPassword' && (
|
{currentStep === 'enterPassword' && (
|
||||||
<EnterPasswordForm
|
<EnterPasswordForm
|
||||||
authReturnUrl={authReturnUrlOrDefault}
|
authFactory={authFactory}
|
||||||
loginRegisterValue={loginRegisterValue}
|
loginRegisterValue={loginRegisterValue}
|
||||||
countryCode={countryCode}
|
countryCode={countryCode}
|
||||||
authType={authType}
|
authType={authType}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import type { AuthType } from '../../types/authTypes';
|
import type { AuthFactory, AuthType } from '../../types/authTypes';
|
||||||
import type { CountryCode } from '@/types/commonTypes';
|
import type { CountryCode } from '@/types/commonTypes';
|
||||||
import {
|
import {
|
||||||
loginWithPassword,
|
loginWithPassword,
|
||||||
@@ -22,17 +22,23 @@ import type { LoginResult, PasswordLoginRequest } from '../../types/userTypes';
|
|||||||
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
||||||
import { useApi } from '@/hooks/useApi';
|
import { useApi } from '@/hooks/useApi';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { generateTokenWithPassword } from '../../api/identityAPI';
|
import {
|
||||||
|
generateTokenWithPassword,
|
||||||
|
type GenerateTokenResponse,
|
||||||
|
} from '../../api/identityAPI';
|
||||||
|
|
||||||
export interface EnterPasswordFormProps {
|
export interface EnterPasswordFormProps {
|
||||||
onEditValue: () => void;
|
onEditValue: () => void;
|
||||||
onLoginWithOTP: () => void;
|
onLoginWithOTP: () => void;
|
||||||
onLoggedIn: (loginResult: LoginResult) => void;
|
onLoggedIn: (
|
||||||
|
loginResult: LoginResult,
|
||||||
|
tokenResponse: GenerateTokenResponse,
|
||||||
|
) => void;
|
||||||
emailOrPhone: string;
|
emailOrPhone: string;
|
||||||
authType: AuthType;
|
authType: AuthType;
|
||||||
loginRegisterValue: string;
|
loginRegisterValue: string;
|
||||||
countryCode: CountryCode;
|
countryCode: CountryCode;
|
||||||
authReturnUrl: string;
|
authFactory: AuthFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EnterPasswordForm = ({
|
export const EnterPasswordForm = ({
|
||||||
@@ -43,7 +49,7 @@ export const EnterPasswordForm = ({
|
|||||||
authType,
|
authType,
|
||||||
loginRegisterValue,
|
loginRegisterValue,
|
||||||
countryCode,
|
countryCode,
|
||||||
authReturnUrl,
|
authFactory,
|
||||||
}: EnterPasswordFormProps) => {
|
}: EnterPasswordFormProps) => {
|
||||||
const { t } = useTranslation('authentication');
|
const { t } = useTranslation('authentication');
|
||||||
const [passValue, setPassValue] = useState<string>('');
|
const [passValue, setPassValue] = useState<string>('');
|
||||||
@@ -57,7 +63,6 @@ export const EnterPasswordForm = ({
|
|||||||
useApi(sendEmailOtp);
|
useApi(sendEmailOtp);
|
||||||
const { loading: loginWithPassLoading, execute: loginWithPassCall } =
|
const { loading: loginWithPassLoading, execute: loginWithPassCall } =
|
||||||
useApi(loginWithPassword);
|
useApi(loginWithPassword);
|
||||||
const auth = useAuth();
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
const handleBlur = () => {
|
||||||
setInputTouched(true);
|
setInputTouched(true);
|
||||||
@@ -72,7 +77,7 @@ export const EnterPasswordForm = ({
|
|||||||
authType === 'phone' ? countryCode + loginRegisterValue : undefined,
|
authType === 'phone' ? countryCode + loginRegisterValue : undefined,
|
||||||
email: authType === 'email' ? loginRegisterValue : undefined,
|
email: authType === 'email' ? loginRegisterValue : undefined,
|
||||||
password: passValue,
|
password: passValue,
|
||||||
returnUrl: authReturnUrl,
|
returnUrl: authFactory.redirectUrl,
|
||||||
};
|
};
|
||||||
const res = await loginWithPassCall(apiRequest);
|
const res = await loginWithPassCall(apiRequest);
|
||||||
|
|
||||||
@@ -82,12 +87,10 @@ export const EnterPasswordForm = ({
|
|||||||
const tokenRes = await generateTokenWithPassword({
|
const tokenRes = await generateTokenWithPassword({
|
||||||
username: apiRequest.email ?? (apiRequest.phoneNumber as string),
|
username: apiRequest.email ?? (apiRequest.phoneNumber as string),
|
||||||
password: apiRequest.password,
|
password: apiRequest.password,
|
||||||
});
|
client_id: authFactory.clientId,
|
||||||
auth.login({
|
|
||||||
...tokenRes.data,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onLoggedIn(res);
|
onLoggedIn(res, tokenRes.data);
|
||||||
toast({
|
toast({
|
||||||
message: t('verify.youHaveSuccessfullyLoggedIn'),
|
message: t('verify.youHaveSuccessfullyLoggedIn'),
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
@@ -134,7 +137,7 @@ export const EnterPasswordForm = ({
|
|||||||
endIcon={<Icon Component={Edit2} />}
|
endIcon={<Icon Component={Edit2} />}
|
||||||
onClick={onEditValue}
|
onClick={onEditValue}
|
||||||
>
|
>
|
||||||
{emailOrPhone}
|
{authType === 'phone' ? countryCode + emailOrPhone : emailOrPhone}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -10,18 +10,25 @@ import { loginOrSignUpWithGoogle } from '../../api/authorizationAPI';
|
|||||||
import { Google } from 'iconsax-react';
|
import { Google } from 'iconsax-react';
|
||||||
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
||||||
import { useApi } from '@/hooks/useApi';
|
import { useApi } from '@/hooks/useApi';
|
||||||
import { generateTokenWithGoogle } from '../../api/identityAPI';
|
import {
|
||||||
|
generateTokenWithGoogle,
|
||||||
|
type GenerateTokenResponse,
|
||||||
|
} from '../../api/identityAPI';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
|
import type { AuthFactory } from '../../types/authTypes';
|
||||||
|
|
||||||
export interface GoogleAuthenticationProps {
|
export interface GoogleAuthenticationProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
authReturnUrl: string;
|
authFactory: AuthFactory;
|
||||||
onGoogleAuthenticated: (loginResult: LoginResult) => void;
|
onGoogleAuthenticated: (
|
||||||
|
loginResult: LoginResult,
|
||||||
|
tokenResponse: GenerateTokenResponse,
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GoogleAuthentication = ({
|
export const GoogleAuthentication = ({
|
||||||
disabled,
|
disabled,
|
||||||
authReturnUrl,
|
authFactory,
|
||||||
onGoogleAuthenticated,
|
onGoogleAuthenticated,
|
||||||
}: GoogleAuthenticationProps) => {
|
}: GoogleAuthenticationProps) => {
|
||||||
const { t } = useTranslation('authentication');
|
const { t } = useTranslation('authentication');
|
||||||
@@ -29,7 +36,6 @@ export const GoogleAuthentication = ({
|
|||||||
useApi(loginOrSignUpWithGoogle);
|
useApi(loginOrSignUpWithGoogle);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const clientRef = useRef<any>(null);
|
const clientRef = useRef<any>(null);
|
||||||
const auth = useAuth();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
@@ -47,19 +53,19 @@ export const GoogleAuthentication = ({
|
|||||||
callback: async (resp: GoogleCodeClientResponse) => {
|
callback: async (resp: GoogleCodeClientResponse) => {
|
||||||
const apiRequest: LoginOrSignUpWithGoogleRequest = {
|
const apiRequest: LoginOrSignUpWithGoogleRequest = {
|
||||||
idToken: resp.id_token,
|
idToken: resp.id_token,
|
||||||
returnUrl: authReturnUrl,
|
returnUrl: authFactory.redirectUrl,
|
||||||
};
|
};
|
||||||
const res = await loginWithGoogleCall(apiRequest);
|
const res = await loginWithGoogleCall(apiRequest);
|
||||||
|
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
const tokenRes = await generateTokenWithGoogle(apiRequest);
|
const tokenRes = await generateTokenWithGoogle({
|
||||||
auth.login({
|
...apiRequest,
|
||||||
...tokenRes.data,
|
client_id: authFactory.clientId,
|
||||||
});
|
});
|
||||||
|
|
||||||
onGoogleAuthenticated(res);
|
onGoogleAuthenticated(res, tokenRes.data);
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
message: t('loginForm.googleAuthenticationFailed'),
|
message: t('loginForm.googleAuthenticationFailed'),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Button, Stack, TextField, Typography } from '@mui/material';
|
|||||||
import { useRef, useState, type Dispatch } from 'react';
|
import { useRef, useState, type Dispatch } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { isNumeric } from '@/utils/regexes/isNumeric';
|
import { isNumeric } from '@/utils/regexes/isNumeric';
|
||||||
import type { AuthType } from '../../types/authTypes';
|
import type { AuthFactory, AuthType } from '../../types/authTypes';
|
||||||
import { isEmail } from '@/utils/regexes/isEmail';
|
import { isEmail } from '@/utils/regexes/isEmail';
|
||||||
import { AuthenticationCard } from '../AuthenticationCard';
|
import { AuthenticationCard } from '../AuthenticationCard';
|
||||||
import { CountryCodeSelector } from '../CountryCodeSelector';
|
import { CountryCodeSelector } from '../CountryCodeSelector';
|
||||||
@@ -13,6 +13,7 @@ import { GoogleAuthentication } from './GoogleAuthentication';
|
|||||||
import { isPhoneNumber } from '@/utils/regexes/isValidPhoneNumber';
|
import { isPhoneNumber } from '@/utils/regexes/isValidPhoneNumber';
|
||||||
import { useToast } from '@rkheftan/harmony-ui';
|
import { useToast } from '@rkheftan/harmony-ui';
|
||||||
import { useApi } from '@/hooks/useApi';
|
import { useApi } from '@/hooks/useApi';
|
||||||
|
import type { GenerateTokenResponse } from '../../api/identityAPI';
|
||||||
|
|
||||||
export interface LoginRegisterFormProps {
|
export interface LoginRegisterFormProps {
|
||||||
loginRegisterValue: string;
|
loginRegisterValue: string;
|
||||||
@@ -22,8 +23,11 @@ export interface LoginRegisterFormProps {
|
|||||||
authType: AuthType;
|
authType: AuthType;
|
||||||
setAuthType: Dispatch<AuthType>;
|
setAuthType: Dispatch<AuthType>;
|
||||||
onLoginRegisterSubmit: (value: string, userStatus: UserStatus) => void;
|
onLoginRegisterSubmit: (value: string, userStatus: UserStatus) => void;
|
||||||
authReturnUrl: string;
|
onGoogleAuthenticated: (
|
||||||
onGoogleAuthenticated: (loginResult: LoginResult) => void;
|
loginResult: LoginResult,
|
||||||
|
tokenResponse: GenerateTokenResponse,
|
||||||
|
) => void;
|
||||||
|
authFactory: AuthFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LoginRegisterForm({
|
export function LoginRegisterForm({
|
||||||
@@ -34,8 +38,8 @@ export function LoginRegisterForm({
|
|||||||
authType,
|
authType,
|
||||||
setAuthType,
|
setAuthType,
|
||||||
onLoginRegisterSubmit,
|
onLoginRegisterSubmit,
|
||||||
authReturnUrl,
|
|
||||||
onGoogleAuthenticated,
|
onGoogleAuthenticated,
|
||||||
|
authFactory,
|
||||||
}: LoginRegisterFormProps) {
|
}: LoginRegisterFormProps) {
|
||||||
const { t } = useTranslation('authentication');
|
const { t } = useTranslation('authentication');
|
||||||
const textFieldRef = useRef<HTMLDivElement>(null);
|
const textFieldRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -156,7 +160,7 @@ export function LoginRegisterForm({
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<GoogleAuthentication
|
<GoogleAuthentication
|
||||||
authReturnUrl={authReturnUrl}
|
authFactory={authFactory}
|
||||||
onGoogleAuthenticated={onGoogleAuthenticated}
|
onGoogleAuthenticated={onGoogleAuthenticated}
|
||||||
disabled={userStatusLoading}
|
disabled={userStatusLoading}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Box, Button, Stack, Typography } from '@mui/material';
|
import { Box, Button, Stack, Typography } from '@mui/material';
|
||||||
import { Edit2 } from 'iconsax-react';
|
import { Edit2 } from 'iconsax-react';
|
||||||
import DigitInput from '@/components/DigitsInput';
|
import DigitInput from '@/components/DigitsInput';
|
||||||
import type { AuthMode, AuthType } from '../../types/authTypes';
|
import type { AuthFactory, AuthMode, AuthType } from '../../types/authTypes';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { AuthenticationCard } from '../AuthenticationCard';
|
import { AuthenticationCard } from '../AuthenticationCard';
|
||||||
import type { LoginRequest, LoginResult } from '../../types/userTypes';
|
import type { LoginRequest, LoginResult } from '../../types/userTypes';
|
||||||
@@ -14,7 +14,10 @@ import {
|
|||||||
import type { CountryCode } from '@/types/commonTypes';
|
import type { CountryCode } from '@/types/commonTypes';
|
||||||
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
||||||
import { useApi } from '@/hooks/useApi';
|
import { useApi } from '@/hooks/useApi';
|
||||||
import { generateTokenWithOtp } from '../../api/identityAPI';
|
import {
|
||||||
|
generateTokenWithOtp,
|
||||||
|
type GenerateTokenResponse,
|
||||||
|
} from '../../api/identityAPI';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
|
|
||||||
interface OtpVerifyFormProps {
|
interface OtpVerifyFormProps {
|
||||||
@@ -23,8 +26,11 @@ interface OtpVerifyFormProps {
|
|||||||
authType: AuthType;
|
authType: AuthType;
|
||||||
authMode: AuthMode;
|
authMode: AuthMode;
|
||||||
onEditValue: () => void;
|
onEditValue: () => void;
|
||||||
onOTPVerified: (loginResult: LoginResult) => void;
|
onOTPVerified: (
|
||||||
authReturnUrl: string;
|
loginResult: LoginResult,
|
||||||
|
tokenResponse: GenerateTokenResponse,
|
||||||
|
) => void;
|
||||||
|
authFactory: AuthFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OtpVerifyForm({
|
export function OtpVerifyForm({
|
||||||
@@ -34,7 +40,7 @@ export function OtpVerifyForm({
|
|||||||
authMode,
|
authMode,
|
||||||
onEditValue,
|
onEditValue,
|
||||||
onOTPVerified,
|
onOTPVerified,
|
||||||
authReturnUrl,
|
authFactory,
|
||||||
}: OtpVerifyFormProps) {
|
}: OtpVerifyFormProps) {
|
||||||
const [otpCode, setOtpCode] = useState<string>('');
|
const [otpCode, setOtpCode] = useState<string>('');
|
||||||
const [otpDigitInvalid, setOtpDigitInvalid] = useState<boolean>(false);
|
const [otpDigitInvalid, setOtpDigitInvalid] = useState<boolean>(false);
|
||||||
@@ -49,7 +55,6 @@ export function OtpVerifyForm({
|
|||||||
useApi(sendEmailOtp);
|
useApi(sendEmailOtp);
|
||||||
const { loading: loginSignUpLoading, execute: loginSignUpCall } =
|
const { loading: loginSignUpLoading, execute: loginSignUpCall } =
|
||||||
useApi(loginOrSignUpWithOtp);
|
useApi(loginOrSignUpWithOtp);
|
||||||
const auth = useAuth();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let interval: NodeJS.Timeout;
|
let interval: NodeJS.Timeout;
|
||||||
@@ -96,7 +101,7 @@ export function OtpVerifyForm({
|
|||||||
otpCode: otpCode,
|
otpCode: otpCode,
|
||||||
phoneNumber: authType === 'phone' ? countryCode + value : undefined,
|
phoneNumber: authType === 'phone' ? countryCode + value : undefined,
|
||||||
email: authType === 'email' ? value : undefined,
|
email: authType === 'email' ? value : undefined,
|
||||||
returnUrl: authReturnUrl,
|
returnUrl: authFactory.redirectUrl,
|
||||||
};
|
};
|
||||||
const res = await loginSignUpCall(loginRequest);
|
const res = await loginSignUpCall(loginRequest);
|
||||||
|
|
||||||
@@ -111,12 +116,10 @@ export function OtpVerifyForm({
|
|||||||
email: loginRequest.email,
|
email: loginRequest.email,
|
||||||
phonenumber: loginRequest.phoneNumber,
|
phonenumber: loginRequest.phoneNumber,
|
||||||
otp: loginRequest.otpCode,
|
otp: loginRequest.otpCode,
|
||||||
});
|
client_id: authFactory.clientId,
|
||||||
auth.login({
|
|
||||||
...tokenRes.data,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onOTPVerified(res);
|
onOTPVerified(res, tokenRes.data);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
message:
|
message:
|
||||||
|
|||||||
@@ -8,3 +8,10 @@ export type AuthStep =
|
|||||||
| 'enterPassword'
|
| 'enterPassword'
|
||||||
| 'addPhoneNumber'
|
| 'addPhoneNumber'
|
||||||
| 'addedPhoneNumberVerify';
|
| 'addedPhoneNumberVerify';
|
||||||
|
|
||||||
|
export interface AuthFactory {
|
||||||
|
clientId: string;
|
||||||
|
redirectUrl: string;
|
||||||
|
isCurrentApplication: () => boolean;
|
||||||
|
getFullRedirectUrl: (token: string) => string;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user