Merge pull request #30 from rkheftan/feat/multiple-client-id

Feat/multiple client
This commit is contained in:
SajadMRjl
2025-08-30 19:20:25 +03:30
committed by GitHub
8 changed files with 156 additions and 70 deletions

3
.env
View File

@@ -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/

View File

@@ -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>(

View File

@@ -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}

View File

@@ -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>

View File

@@ -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'),

View File

@@ -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}
/> />

View File

@@ -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:

View File

@@ -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;
}