@truworth/twc-auth 1.2.5 → 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/assets/email_icon.svg +6 -0
- package/build/src/constants/social-login-options.js +16 -0
- package/build/src/contexts/AuthContext.js +4 -2
- package/build/src/enums/index.js +1 -0
- package/build/src/enums/socialLogins.enum.js +6 -0
- package/build/src/helpers/{Network.js → network.js} +2 -2
- package/build/src/hooks/useRequest.js +1 -1
- package/build/src/screens/EnterEmail/hooks/internal/useEnterEmail.js +0 -1
- package/build/src/screens/EnterEmail/index.js +31 -3
- package/build/src/screens/EnterPassword/hooks/internal/useEnterPassword.js +19 -4
- package/build/src/screens/EnterPassword/index.js +27 -8
- package/build/src/screens/EnterPassword/index.native.js +10 -2
- package/build/src/screens/LoginWithEmailOTP/hooks/internal/useLoginWithEmailOTP.js +22 -10
- package/build/src/screens/LoginWithEmailOTP/index.js +2 -2
- package/build/src/screens/LoginWithEmailOTP/index.native.js +2 -2
- package/build/src/screens/SSOLogin/SSOCallback/components/SSOCallbackComponent/index.js +1 -1
- package/build/src/screens/SignUp/components/SignUpWebComponent/index.js +11 -1
- package/build/src/screens/SignUp/hooks/internal/useSignUp.js +1 -1
- package/build/src/screens/SignUp/index.native.js +3 -0
- package/build/src/screens/UserConsent/hooks/internal/useConsent.js +10 -1
- package/build/src/screens/VerifyEmail/hooks/internal/useVerifyEmail.js +2 -2
- package/build/src/screens/VerifyResetPasswordOTP/hooks/internal/useVerifyResetPasswordOTP.js +1 -1
- package/build/src/screens/Welcome/SocialAuth/commonSocialAuth.js +127 -0
- package/build/src/screens/Welcome/SocialAuth/hooks/useSocialAuth.native.js +191 -0
- package/build/src/screens/Welcome/SocialAuth/hooks/useSocialAuth.web.js +13 -0
- package/build/src/screens/Welcome/SocialAuth/hooks/web/useFacebookAuth.web.js +118 -0
- package/build/src/screens/Welcome/SocialAuth/hooks/web/useGoogleAuth.web.js +75 -0
- package/build/src/screens/Welcome/components/SocialLoginModal/index.js +9 -0
- package/build/src/screens/Welcome/components/SocialLoginModal/index.native.js +21 -0
- package/build/src/screens/Welcome/components/SocialLoginModal/types.js +1 -0
- package/build/src/screens/Welcome/hooks/internal/useWelcome.js +10 -5
- package/build/src/screens/Welcome/index.native.js +41 -4
- package/build/types/constants/social-login-options.d.ts +3 -0
- package/build/types/contexts/AuthContext.d.ts +2 -1
- package/build/types/contexts/type.d.ts +9 -0
- package/build/types/enums/index.d.ts +1 -0
- package/build/types/enums/socialLogins.enum.d.ts +5 -0
- package/build/types/helpers/types.d.ts +7 -1
- package/build/types/navigator/index.native.d.ts +6 -0
- package/build/types/screens/EnterEmail/hooks/internal/useEnterEmail.d.ts +0 -1
- package/build/types/screens/EnterPassword/hooks/internal/useEnterPassword.d.ts +5 -1
- package/build/types/screens/EnterPassword/types.d.ts +3 -0
- package/build/types/screens/LoginWithEmailOTP/hooks/internal/useLoginWithEmailOTP.d.ts +4 -1
- package/build/types/screens/LoginWithEmailOTP/index.d.ts +4 -1
- package/build/types/screens/Welcome/SocialAuth/commonSocialAuth.d.ts +50 -0
- package/build/types/screens/Welcome/SocialAuth/hooks/useSocialAuth.native.d.ts +7 -0
- package/build/types/screens/Welcome/SocialAuth/hooks/useSocialAuth.web.d.ts +7 -0
- package/build/types/screens/Welcome/SocialAuth/hooks/web/useFacebookAuth.web.d.ts +5 -0
- package/build/types/screens/Welcome/SocialAuth/hooks/web/useGoogleAuth.web.d.ts +4 -0
- package/build/types/screens/Welcome/components/SocialLoginModal/index.d.ts +3 -0
- package/build/types/screens/Welcome/components/SocialLoginModal/index.native.d.ts +3 -0
- package/build/types/screens/Welcome/components/SocialLoginModal/types.d.ts +8 -0
- package/build/types/screens/Welcome/hooks/internal/useWelcome.d.ts +8 -0
- package/build/types/types/types.d.ts +3 -0
- package/package.json +18 -16
- package/build/assets/okay_icon copy.svg +0 -3
- /package/build/src/helpers/{Validation.js → validation.js} +0 -0
- /package/build/types/helpers/{Network.d.ts → network.d.ts} +0 -0
- /package/build/types/helpers/{Validation.d.ts → validation.d.ts} +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="18" viewBox="0 0 22 18">
|
|
2
|
+
<g fill="#ACD7D3">
|
|
3
|
+
<path d="M6.058 5.341c0 .568-.462 1.031-1.032 1.031H2.62c-.57 0-1.032-.463-1.032-1.03 0-.57.463-1.033 1.032-1.033h2.406c.57 0 1.032.463 1.032 1.032zM8.78 8.97c0 .568-.462 1.031-1.033 1.031H5.341c-.569 0-1.032-.463-1.032-1.03 0-.57.463-1.033 1.032-1.033h2.406c.57 0 1.032.463 1.032 1.032zM11.274 12.6c0 .567-.462 1.03-1.032 1.03H7.836c-.569 0-1.032-.463-1.032-1.03 0-.57.463-1.033 1.032-1.033h2.406c.57 0 1.032.463 1.032 1.032z"/>
|
|
4
|
+
<path d="M18.448.005H3.462C3.454.005 3.447 0 3.438 0H1.032C.462 0 0 .463 0 1.032 0 1.6.463 2.063 1.032 2.063h4.126l5.277 5.347c1.375 1.392 3.623 1.407 5.016.033l4.406-4.349c.048.145.08.297.08.457v10.361c0 .82-.668 1.488-1.489 1.488h-6.232c-.016 0-.03-.01-.047-.01H9.763c-.57 0-1.032.464-1.032 1.033 0 .568.463 1.03 1.032 1.03h.595v.006h8.09c1.956 0 3.547-1.59 3.547-3.547V3.552c0-1.957-1.59-3.547-3.547-3.547zm-4.443 5.973c-.584.576-1.529.57-2.105-.014L8.05 2.063h9.92l-3.965 3.915z"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SocialLoginsEnum } from "../enums";
|
|
2
|
+
const socialLoginOptions = [
|
|
3
|
+
{
|
|
4
|
+
type: SocialLoginsEnum.Google,
|
|
5
|
+
iconImageUrl: `https://cdn-thewellnesscorner.s3.amazonaws.com/twc-web-images/template/google-icon.png`,
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
type: SocialLoginsEnum.Facebook,
|
|
9
|
+
iconImageUrl: `https://cdn-thewellnesscorner.s3.amazonaws.com/twc-web-images/template/facebook-icon.png`,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
type: SocialLoginsEnum.Apple,
|
|
13
|
+
iconImageUrl: `https://cdn-thewellnesscorner.s3.amazonaws.com/twc-web-images/template/apple-icon.png`,
|
|
14
|
+
}
|
|
15
|
+
];
|
|
16
|
+
export { socialLoginOptions };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
|
3
3
|
import { MEMBER_IMAGES_URL } from '../constants/cdn-url';
|
|
4
|
-
import { createHttpClient } from '../helpers/
|
|
4
|
+
import { createHttpClient } from '../helpers/network';
|
|
5
5
|
/**
|
|
6
6
|
* Public Auth Authentication Context
|
|
7
7
|
*
|
|
@@ -32,8 +32,9 @@ const AuthPackageContext = React.createContext(null);
|
|
|
32
32
|
* @param onLaunchAuthSession - Optional callback after successfully launching the auth session. For example, it can be used to set the Firebase notification token or request Android permissions.
|
|
33
33
|
* @param onRefreshSession - Optional handler for refreshing user session
|
|
34
34
|
* @param openChatSupport - Optional callback to open chat support
|
|
35
|
+
* @param socialLoginConfig - Optional social login configuration
|
|
35
36
|
*/
|
|
36
|
-
const AuthProvider = ({ children, LogoComponent, session, appConfig, onLogin, onLogout, onLaunchAuthSession, onRefreshSession, openChatSupport }) => {
|
|
37
|
+
const AuthProvider = ({ children, LogoComponent, session, appConfig, onLogin, onLogout, onLaunchAuthSession, onRefreshSession, openChatSupport, socialLoginConfig }) => {
|
|
37
38
|
const [isLoadingProfile, setIsLoadingProfile] = useState(false);
|
|
38
39
|
const [profile, setProfile] = useState(null);
|
|
39
40
|
const [client, setClient] = useState(null);
|
|
@@ -171,6 +172,7 @@ const AuthProvider = ({ children, LogoComponent, session, appConfig, onLogin, on
|
|
|
171
172
|
LogoComponent,
|
|
172
173
|
appConfig: appConfigValue,
|
|
173
174
|
registrationMethod,
|
|
175
|
+
socialLoginConfig,
|
|
174
176
|
onLaunchAuthSession,
|
|
175
177
|
onLogin,
|
|
176
178
|
logout,
|
package/build/src/enums/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { axiosClient } from '../api/axiosClient';
|
|
2
|
-
import { showMessage } from '
|
|
2
|
+
import { showMessage } from './show-message';
|
|
3
3
|
import {} from 'axios';
|
|
4
4
|
export const createHttpClient = ({ token, onRefreshSession, onLogout }) => {
|
|
5
5
|
let currentToken = token;
|
|
@@ -80,7 +80,7 @@ export const createHttpClient = ({ token, onRefreshSession, onLogout }) => {
|
|
|
80
80
|
showMessage({ message: 'Your session has expired. Please login again.' });
|
|
81
81
|
}
|
|
82
82
|
catch (err) {
|
|
83
|
-
console.
|
|
83
|
+
console.log('Error logging out:', err);
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
86
|
return { request };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { showMessage } from '../helpers/show-message';
|
|
3
3
|
import { useAuthPackageContext } from './internal/useAuthPackageContext';
|
|
4
|
-
import { createHttpClient } from '../helpers/
|
|
4
|
+
import { createHttpClient } from '../helpers/network';
|
|
5
5
|
const useRequest = () => {
|
|
6
6
|
const { token: sessionToken, logout, refreshSession } = useAuthPackageContext();
|
|
7
7
|
const [token, setToken] = useState(sessionToken);
|
|
@@ -69,7 +69,6 @@ const useEnterEmail = () => {
|
|
|
69
69
|
handleClearEmail,
|
|
70
70
|
isLoginConflictModalVisible,
|
|
71
71
|
setIsLoginConflictModalVisible,
|
|
72
|
-
onRegistrationMethodChange,
|
|
73
72
|
appName: appConfig.appName,
|
|
74
73
|
loginConflictTitle: `Sign in with ${loginType}`,
|
|
75
74
|
loginConflictDescription: `Looks like you previously registered using ${loginType}. Please go back and sign in with ${loginType}.`,
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Button, Form, TextInput, useForm } from '@truworth/twc-web-design';
|
|
2
|
+
import { Button, Flex, Form, TextInput, Typography, useForm } from '@truworth/twc-web-design';
|
|
3
3
|
import { ConfirmationModal as LoginConflictModal } from '../../components/ConfirmationModal';
|
|
4
4
|
import { ScreenLayout } from "../../components/ScreenLayout";
|
|
5
5
|
import { useEnterEmail } from "./hooks/internal/useEnterEmail";
|
|
6
6
|
import { SupportDetails } from '../../components/SupportDetails';
|
|
7
|
+
import { useWelcome } from '../Welcome/hooks/internal/useWelcome';
|
|
8
|
+
import { socialLoginOptions } from '../../constants/social-login-options';
|
|
9
|
+
import { SocialLoginModal } from '../Welcome/components/SocialLoginModal';
|
|
10
|
+
import { useSocialAuth } from '../Welcome/SocialAuth/hooks/useSocialAuth.web';
|
|
11
|
+
import { SocialLoginsEnum } from '../../enums';
|
|
12
|
+
import _ from 'lodash';
|
|
7
13
|
const EnterEmail = ({ onContinue, onPressSignInWithSSO }) => {
|
|
8
|
-
const {
|
|
14
|
+
const { loginWithGoogle, loginWithFacebook } = useSocialAuth();
|
|
15
|
+
const { email, isEmailValid, handleEmailChange, appName, handleEmailExists, loading, loginConflictTitle, loginConflictDescription, isLoginConflictModalVisible, setIsLoginConflictModalVisible, } = useEnterEmail();
|
|
16
|
+
const { showSocialLoginModal, setShowSocialLoginModal, socialLoginType, setSocialLoginType, isSocialLoginEnabled, socialLoginConfig, } = useWelcome();
|
|
9
17
|
const form = useForm({ defaultValues: { email } });
|
|
18
|
+
const handleLogin = () => {
|
|
19
|
+
setShowSocialLoginModal(false);
|
|
20
|
+
switch (socialLoginType) {
|
|
21
|
+
case SocialLoginsEnum.Google:
|
|
22
|
+
return loginWithGoogle();
|
|
23
|
+
case SocialLoginsEnum.Facebook:
|
|
24
|
+
return loginWithFacebook();
|
|
25
|
+
default:
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
10
29
|
return (_jsxs(_Fragment, { children: [_jsxs(ScreenLayout, { title: `Hi, Welcome To ${appName}!`, subTitle: "Please enter email to get started.", buttonProps: {
|
|
11
30
|
loading,
|
|
12
31
|
label: 'Continue',
|
|
@@ -21,6 +40,15 @@ const EnterEmail = ({ onContinue, onPressSignInWithSSO }) => {
|
|
|
21
40
|
handleEmailChange(e.target.value);
|
|
22
41
|
}
|
|
23
42
|
}) }) }) }), isLoginConflictModalVisible &&
|
|
24
|
-
_jsx(LoginConflictModal, { title: loginConflictTitle, description: loginConflictDescription, visible: isLoginConflictModalVisible, onProceed: () => setIsLoginConflictModalVisible(false), onClose: () => setIsLoginConflictModalVisible(false), primaryLabel: 'Okay' })] }), _jsx(Button, { isFullWidth: true, type: "button", label: 'Sign In with SSO', variant: 'secondary', className: 'flex-none mt-6', onClick: onPressSignInWithSSO }), _jsx(
|
|
43
|
+
_jsx(LoginConflictModal, { title: loginConflictTitle, description: loginConflictDescription, visible: isLoginConflictModalVisible, onProceed: () => setIsLoginConflictModalVisible(false), onClose: () => setIsLoginConflictModalVisible(false), primaryLabel: 'Okay' })] }), _jsx(Button, { isFullWidth: true, type: "button", label: 'Sign In with SSO', variant: 'secondary', className: 'flex-none mt-6', onClick: onPressSignInWithSSO }), isSocialLoginEnabled && (_jsxs(_Fragment, { children: [_jsxs(Flex, { align: "center", className: "my-6", children: [_jsx(Flex, { className: "flex-1 border-t border-gray-300" }), _jsx(Typography, { type: 'body', size: 'small', className: "text-gray-500 px-4", children: "or continue with" }), _jsx(Flex, { className: "flex-1 border-t border-gray-300" })] }), _jsx(Flex, { justify: 'center', align: 'center', className: "gap-4", children: socialLoginOptions.map(({ type, iconImageUrl }) => {
|
|
44
|
+
if (type === SocialLoginsEnum.Apple) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return socialLoginConfig?.[type]?.enabled && (_jsxs("button", { type: "button", className: "flex justify-center p-3 gap-2 items-center\n rounded-[50px] flex-1 border text-gray-800 hover:text-gray-800\n border-gray-100 bg-gray-100 hover:border-gray-300 transition-all duration-300 cursor-pointer", onClick: () => {
|
|
48
|
+
setSocialLoginType(type);
|
|
49
|
+
setShowSocialLoginModal(true);
|
|
50
|
+
}, children: [_jsx("img", { src: iconImageUrl, style: { width: 20, height: 20 } }), _jsx(Typography, { type: 'utility', size: 'medium', children: _.startCase(type) })] }, type));
|
|
51
|
+
}) })] })), showSocialLoginModal &&
|
|
52
|
+
_jsx(SocialLoginModal, { visible: showSocialLoginModal, hide: () => setShowSocialLoginModal(false), socialLoginType: socialLoginType, onClick: handleLogin }), _jsx(SupportDetails, {})] }));
|
|
25
53
|
};
|
|
26
54
|
export default EnterEmail;
|
|
@@ -17,16 +17,31 @@ const useEnterPassword = () => {
|
|
|
17
17
|
const handlePasswordChange = useCallback((value) => {
|
|
18
18
|
setPassword(value);
|
|
19
19
|
}, []);
|
|
20
|
-
const handleSubmit = useCallback(async ({ email, source }) => {
|
|
20
|
+
const handleSubmit = useCallback(async ({ email, source, verifyEmailOTP }) => {
|
|
21
21
|
setLoading(true);
|
|
22
22
|
try {
|
|
23
|
-
const response = await axiosClient.post("/auth/login", {
|
|
23
|
+
const response = await axiosClient.post("/auth/login/v2", {
|
|
24
24
|
email,
|
|
25
25
|
password,
|
|
26
26
|
source,
|
|
27
27
|
});
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Check if MFA is enabled
|
|
29
|
+
if (response.data.mfaEnabled && response.data.sessionToken) {
|
|
30
|
+
// Navigate to LoginWithEmailOTP screen with sessionToken
|
|
31
|
+
if (verifyEmailOTP) {
|
|
32
|
+
verifyEmailOTP({
|
|
33
|
+
mfaEnabled: response.data.mfaEnabled,
|
|
34
|
+
sessionToken: response.data.sessionToken
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
setLoading(false);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Normal login flow (no MFA)
|
|
41
|
+
if (response.data.token) {
|
|
42
|
+
onTokenChange(response.data.token);
|
|
43
|
+
onLogin(response.data);
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
catch (error) {
|
|
32
47
|
const errorMessage = error?.response?.data?.errors?.[0]?.message;
|
|
@@ -9,28 +9,47 @@ import VerifyResetPasswordOTP from '../VerifyResetPasswordOTP';
|
|
|
9
9
|
const EnterPassword = ({ email, onPressBack, onVerifiedResetPasswordOTP }) => {
|
|
10
10
|
const [showResetPasswordOTPModal, setShowResetPasswordOTPModal] = useState(false);
|
|
11
11
|
const [showLoginWithEmailOTPModal, setShowLoginWithEmailOTPModal] = useState(false);
|
|
12
|
+
const [mfaSessionToken, setMfaSessionToken] = useState(undefined);
|
|
13
|
+
const [mfaEnabled, setMfaEnabled] = useState(false);
|
|
12
14
|
const { loading, password, handlePasswordChange, handleSubmit, appName } = useEnterPassword();
|
|
13
15
|
const form = useForm({ defaultValues: { password } });
|
|
14
16
|
return (_jsxs(_Fragment, { children: [_jsxs(ScreenLayout, { title: `Hi, Welcome To ${appName}!`, subTitle: email, buttonProps: {
|
|
15
17
|
loading,
|
|
16
18
|
label: 'Continue',
|
|
17
|
-
onClick: () => {
|
|
18
|
-
|
|
19
|
+
onClick: () => {
|
|
20
|
+
if (email)
|
|
21
|
+
handleSubmit({
|
|
22
|
+
email,
|
|
23
|
+
source: 'web',
|
|
24
|
+
verifyEmailOTP: ({ mfaEnabled, sessionToken }) => {
|
|
25
|
+
setMfaSessionToken(sessionToken);
|
|
26
|
+
setMfaEnabled(mfaEnabled);
|
|
27
|
+
setShowLoginWithEmailOTPModal(true);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
},
|
|
19
31
|
disabled: !password || !email || loading,
|
|
20
|
-
}, onPressBack: onPressBack, children: [_jsx(Form, { className: "w-full", form: form,
|
|
21
|
-
handleSubmit({ email, source: 'web' }); }, children: _jsx(Form.Item, { name: "password", label: "Password", children: _jsx(PasswordInput, { placeholder: "Enter your password", value: password, showStrengthIndicator: false, className: `border rounded-md px-3 py-2 border-gray-300`, ...form.register('password', {
|
|
32
|
+
}, onPressBack: onPressBack, children: [_jsx(Form, { className: "w-full", form: form, children: _jsx(Form.Item, { name: "password", label: "Password", children: _jsx(PasswordInput, { placeholder: "Enter your password", value: password, showStrengthIndicator: false, className: `border rounded-md px-3 py-2 border-gray-300`, ...form.register('password', {
|
|
22
33
|
onChange: (e) => {
|
|
23
34
|
handlePasswordChange(e.target.value);
|
|
24
35
|
}
|
|
25
|
-
}) }) }) }), _jsx(Button, { type: "button", size: 'small', variant: 'link', label: "Forgot Password?", className: "px-0 h-auto ml-auto block mt-4", onClick: () => setShowResetPasswordOTPModal(true) })] }), _jsxs(Flex, { align: "center", className: "mt-6 mb-6", children: [_jsx(Flex, { className: "flex-1 border-t border-gray-300" }), _jsx(Typography, { type: 'body', size: 'small', className: "text-gray-500 px-4", children: "OR" }), _jsx(Flex, { className: "flex-1 border-t border-gray-300" })] }), _jsx(Button, { label: 'Sign In with Email OTP', variant: 'secondary', className: 'flex-none', isFullWidth: true, type: "button", onClick: () =>
|
|
36
|
+
}) }) }) }), _jsx(Button, { type: "button", size: 'small', variant: 'link', label: "Forgot Password?", className: "px-0 h-auto ml-auto block mt-4", onClick: () => setShowResetPasswordOTPModal(true) })] }), _jsxs(Flex, { align: "center", className: "mt-6 mb-6", children: [_jsx(Flex, { className: "flex-1 border-t border-gray-300" }), _jsx(Typography, { type: 'body', size: 'small', className: "text-gray-500 px-4", children: "OR" }), _jsx(Flex, { className: "flex-1 border-t border-gray-300" })] }), _jsx(Button, { label: 'Sign In with Email OTP', variant: 'secondary', className: 'flex-none', isFullWidth: true, type: "button", onClick: () => {
|
|
37
|
+
setMfaSessionToken(undefined);
|
|
38
|
+
setMfaEnabled(false);
|
|
39
|
+
setShowLoginWithEmailOTPModal(true);
|
|
40
|
+
} }), _jsx(SupportDetails, {}), showResetPasswordOTPModal &&
|
|
26
41
|
_jsx(VerifyResetPasswordOTPModal, { email: email, show: showResetPasswordOTPModal, hide: () => setShowResetPasswordOTPModal(false), onVerifiedOTP: (sessionToken) => {
|
|
27
42
|
setShowResetPasswordOTPModal(false);
|
|
28
43
|
onVerifiedResetPasswordOTP(sessionToken);
|
|
29
44
|
} }), showLoginWithEmailOTPModal &&
|
|
30
|
-
_jsx(LoginWithEmailOTPModal, { email: email, show: showLoginWithEmailOTPModal, hide: () =>
|
|
45
|
+
_jsx(LoginWithEmailOTPModal, { email: email, password: password, sessionToken: mfaSessionToken, mfaEnabled: mfaEnabled, show: showLoginWithEmailOTPModal, hide: () => {
|
|
46
|
+
setShowLoginWithEmailOTPModal(false);
|
|
47
|
+
setMfaSessionToken(undefined);
|
|
48
|
+
setMfaEnabled(false);
|
|
49
|
+
} })] }));
|
|
31
50
|
};
|
|
32
|
-
const LoginWithEmailOTPModal = ({ show, hide, email }) => {
|
|
33
|
-
return (_jsx(ResponsiveModal, { open: show, onClose: hide, title: _jsx(Typography, { type: "heading", size: "h6", className: "text-center mb-0", children: "Verify Your Email" }), showCloseButton: false, maskClosable: false, size: 'sm', children: _jsx(LoginWithEmailOTP, { email: email }) }));
|
|
51
|
+
const LoginWithEmailOTPModal = ({ show, hide, email, password, sessionToken, mfaEnabled }) => {
|
|
52
|
+
return (_jsx(ResponsiveModal, { open: show, onClose: hide, title: _jsx(Typography, { type: "heading", size: "h6", className: "text-center mb-0", children: "Verify Your Email" }), showCloseButton: false, maskClosable: false, size: 'sm', children: _jsx(LoginWithEmailOTP, { email: email, password: password, sessionToken: sessionToken, mfaEnabled: mfaEnabled }) }));
|
|
34
53
|
};
|
|
35
54
|
const VerifyResetPasswordOTPModal = ({ show, hide, email, onVerifiedOTP }) => {
|
|
36
55
|
return (_jsx(ResponsiveModal, { title: "Reset Your Password", open: show, onClose: hide, maskClosable: false, showCloseButton: false, size: 'sm', children: _jsx(VerifyResetPasswordOTP, { email: email, onVerifiedOTP: onVerifiedOTP }) }));
|
|
@@ -11,8 +11,16 @@ const EnterPassword = ({ navigation, route }) => {
|
|
|
11
11
|
return (_jsxs(Layout, { style: { flex: 1, backgroundColor: primary.white }, children: [_jsxs(ScreenLayout, { title: "Enter Your Password", buttonProps: {
|
|
12
12
|
loading,
|
|
13
13
|
label: 'Continue',
|
|
14
|
-
onPress: () => {
|
|
15
|
-
|
|
14
|
+
onPress: () => {
|
|
15
|
+
if (email)
|
|
16
|
+
handleSubmit({
|
|
17
|
+
email,
|
|
18
|
+
source: Platform.OS,
|
|
19
|
+
verifyEmailOTP: ({ mfaEnabled, sessionToken }) => {
|
|
20
|
+
navigation.navigate('LoginWithEmailOTP', { email, password, mfaEnabled, sessionToken });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
},
|
|
16
24
|
disabled: isDisabled,
|
|
17
25
|
}, children: [_jsx(View, { style: { position: 'relative' }, children: _jsx(TextInputField, { placeholder: 'Enter password...', onChangeValue: handlePasswordChange, secureTextEntry: !isPasswordVisible, value: password, rightIcon2: isPasswordVisible ? 'eye-outline' : 'eye-off-outline', onPressRightIcon2: () => togglePasswordVisibility(), autoFocus: true }) }), _jsx(TouchableOpacity, { activeOpacity: 0.8, style: { marginTop: 10, alignSelf: 'flex-end' }, onPress: () => navigation.navigate('VerifyResetPasswordOTP', { email }), children: _jsx(Text, { style: {
|
|
18
26
|
fontSize: 12, fontWeight: '600',
|
|
@@ -6,29 +6,33 @@ import { axiosClient } from "../../../../api/axiosClient";
|
|
|
6
6
|
* @internal
|
|
7
7
|
* Hook for handling Email OTP Login flow.
|
|
8
8
|
*/
|
|
9
|
-
const useLoginWithEmailOTP = ({ email, source }) => {
|
|
9
|
+
const useLoginWithEmailOTP = ({ email, password, source, sessionToken, mfaEnabled }) => {
|
|
10
10
|
const [resendOTPCounter, setResendOTPCounter] = useState(0);
|
|
11
11
|
const [status, setStatus] = useState("idle");
|
|
12
|
-
const sessionTokenRef = useRef("");
|
|
13
|
-
const { onLogin } = useAuthPackageContext();
|
|
12
|
+
const sessionTokenRef = useRef(sessionToken || "");
|
|
13
|
+
const { onLogin, onTokenChange } = useAuthPackageContext();
|
|
14
14
|
/**
|
|
15
15
|
* Sends an OTP to the provided email.
|
|
16
16
|
*/
|
|
17
17
|
const sendOtp = useCallback(() => {
|
|
18
18
|
setStatus("loading");
|
|
19
|
+
const payload = { email, source };
|
|
20
|
+
if (password && mfaEnabled) {
|
|
21
|
+
payload.password = password;
|
|
22
|
+
}
|
|
19
23
|
axiosClient({
|
|
20
|
-
url: '/auth/login/email',
|
|
24
|
+
url: mfaEnabled ? '/auth/login/v2' : '/auth/login/email',
|
|
21
25
|
method: 'POST',
|
|
22
|
-
data:
|
|
26
|
+
data: payload
|
|
23
27
|
}).then((res) => {
|
|
24
28
|
setResendOTPCounter(prevCounter => prevCounter + 1);
|
|
25
29
|
sessionTokenRef.current = res.data.sessionToken;
|
|
26
30
|
}).catch((error) => {
|
|
27
|
-
console.
|
|
31
|
+
console.log('Failed to send OTP:', error);
|
|
28
32
|
const message = error?.response?.data?.errors?.[0]?.message || "Failed to send OTP. Please try again.";
|
|
29
33
|
showMessage({ message });
|
|
30
34
|
}).finally(() => setStatus("idle"));
|
|
31
|
-
}, [email, source]);
|
|
35
|
+
}, [email, source, password, mfaEnabled]);
|
|
32
36
|
/**
|
|
33
37
|
* Verifies the entered OTP and logs the user in.
|
|
34
38
|
*/
|
|
@@ -41,6 +45,9 @@ const useLoginWithEmailOTP = ({ email, source }) => {
|
|
|
41
45
|
}).then((res) => {
|
|
42
46
|
setStatus('valid');
|
|
43
47
|
const { token, member } = res.data;
|
|
48
|
+
if (token) {
|
|
49
|
+
onTokenChange(token);
|
|
50
|
+
}
|
|
44
51
|
onLogin({ token, member });
|
|
45
52
|
}).catch((error) => {
|
|
46
53
|
const errorCode = error?.response?.status;
|
|
@@ -56,15 +63,20 @@ const useLoginWithEmailOTP = ({ email, source }) => {
|
|
|
56
63
|
return () => clearTimeout(timeoutId);
|
|
57
64
|
}
|
|
58
65
|
});
|
|
59
|
-
}, [
|
|
66
|
+
}, [onLogin, onTokenChange]);
|
|
60
67
|
/**
|
|
61
68
|
* Automatically sends OTP when email and source are provided.
|
|
69
|
+
* Skip if mfaEnabled & sessionToken is already provided (from MFA flow).
|
|
62
70
|
*/
|
|
63
71
|
useEffect(() => {
|
|
64
|
-
if (email && source) {
|
|
72
|
+
if (email && source && !mfaEnabled) {
|
|
65
73
|
sendOtp();
|
|
66
74
|
}
|
|
67
|
-
|
|
75
|
+
else if (mfaEnabled && sessionToken) {
|
|
76
|
+
// If sessionToken is provided, we already have a session, just initialize
|
|
77
|
+
sessionTokenRef.current = sessionToken;
|
|
78
|
+
}
|
|
79
|
+
}, [email, source, sessionToken, sendOtp]);
|
|
68
80
|
return {
|
|
69
81
|
sendOtp,
|
|
70
82
|
verifyOtp,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useLoginWithEmailOTP } from "./hooks/internal/useLoginWithEmailOTP";
|
|
3
3
|
import { VerifyEmailOTP } from "../../components/VerifyEmailOTP";
|
|
4
|
-
const LoginWithEmailOTP = ({ email }) => {
|
|
5
|
-
const { resendOTPCounter, status, verifyOtp, sendOtp, } = useLoginWithEmailOTP({ email, source: 'web' });
|
|
4
|
+
const LoginWithEmailOTP = ({ email, password, sessionToken, mfaEnabled }) => {
|
|
5
|
+
const { resendOTPCounter, status, verifyOtp, sendOtp, } = useLoginWithEmailOTP({ email, password, source: 'web', sessionToken, mfaEnabled });
|
|
6
6
|
return (_jsx(VerifyEmailOTP, { email: email, validateOTP: verifyOtp, resendOTP: sendOtp, status: status, resendOTPCounter: resendOTPCounter }));
|
|
7
7
|
};
|
|
8
8
|
export default LoginWithEmailOTP;
|
|
@@ -3,8 +3,8 @@ import { Platform } from 'react-native';
|
|
|
3
3
|
import { VerifyEmailOTP } from '../../components/VerifyEmailOTP';
|
|
4
4
|
import { useLoginWithEmailOTP } from './hooks/internal/useLoginWithEmailOTP';
|
|
5
5
|
const LoginWithEmailOTP = ({ route }) => {
|
|
6
|
-
const { email } = route.params;
|
|
7
|
-
const { resendOTPCounter, status, verifyOtp, sendOtp, } = useLoginWithEmailOTP({ email, source: Platform.OS });
|
|
6
|
+
const { email, sessionToken, mfaEnabled, password } = route.params;
|
|
7
|
+
const { resendOTPCounter, status, verifyOtp, sendOtp, } = useLoginWithEmailOTP({ email, password, source: Platform.OS, sessionToken, mfaEnabled });
|
|
8
8
|
return (_jsx(VerifyEmailOTP, { email: email, status: status, validateOTP: verifyOtp, resendOTP: sendOtp, resendOTPCounter: resendOTPCounter }));
|
|
9
9
|
};
|
|
10
10
|
export default LoginWithEmailOTP;
|
|
@@ -21,7 +21,7 @@ const SSOCallbackComponents = () => {
|
|
|
21
21
|
setCode(typeof code === 'string' ? code : '');
|
|
22
22
|
}
|
|
23
23
|
catch (error) {
|
|
24
|
-
console.
|
|
24
|
+
console.log('Failed to read from localStorage:', error);
|
|
25
25
|
setCode(typeof code === 'string' ? code : '');
|
|
26
26
|
// Fallback: proceed without stored values
|
|
27
27
|
}
|
|
@@ -11,18 +11,28 @@ const SignUpWebComponent = () => {
|
|
|
11
11
|
const [signUpStep, setSignUpStep] = useState('registration');
|
|
12
12
|
const [userDetails, setUserDetails] = useState({});
|
|
13
13
|
const [registrationToken, setRegistrationToken] = useState('');
|
|
14
|
+
const [fbUserId, setFbUserId] = useState('');
|
|
15
|
+
const [googleUserId, setGoogleUserId] = useState('');
|
|
14
16
|
useEffect(() => {
|
|
15
17
|
const rawRegistrationToken = router.query.registrationToken;
|
|
18
|
+
const rawFbUserId = router.query.fbUserId;
|
|
19
|
+
const rawGoogleUserId = router.query.googleUserId;
|
|
16
20
|
if (typeof rawRegistrationToken === 'string') {
|
|
17
21
|
setRegistrationToken(rawRegistrationToken);
|
|
18
22
|
}
|
|
23
|
+
if (typeof rawFbUserId === 'string') {
|
|
24
|
+
setFbUserId(rawFbUserId);
|
|
25
|
+
}
|
|
26
|
+
if (typeof rawGoogleUserId === 'string') {
|
|
27
|
+
setGoogleUserId(rawGoogleUserId);
|
|
28
|
+
}
|
|
19
29
|
}, [router.query.registrationToken]);
|
|
20
30
|
const renderStep = () => {
|
|
21
31
|
switch (signUpStep) {
|
|
22
32
|
case 'registration':
|
|
23
33
|
return (_jsx(SignUpFormComponent, { userDetails: userDetails, onContinue: (userDetails) => {
|
|
24
34
|
if (registrationToken) {
|
|
25
|
-
setUserDetails({ ...userDetails, registrationToken });
|
|
35
|
+
setUserDetails({ ...userDetails, registrationToken, fbUserId, googleUserId });
|
|
26
36
|
setSignUpStep('user-consent');
|
|
27
37
|
return;
|
|
28
38
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMemo, useState } from "react";
|
|
2
|
-
import { validateEmojiRegex } from "../../../../helpers/
|
|
2
|
+
import { validateEmojiRegex } from "../../../../helpers/validation";
|
|
3
3
|
import { showMessage } from "../../../../helpers/show-message";
|
|
4
4
|
import { useAuthPackageContext } from "../../../../hooks/internal/useAuthPackageContext";
|
|
5
5
|
import { RegistrationMethod } from "../../../../enums";
|
|
@@ -50,6 +50,9 @@ const SignUp = ({ navigation, route }) => {
|
|
|
50
50
|
selectedDOB,
|
|
51
51
|
countryCode,
|
|
52
52
|
referralCode,
|
|
53
|
+
googleUserId: route.params?.googleUserId,
|
|
54
|
+
fbUserId: route.params?.fbUserId,
|
|
55
|
+
appleUserId: route.params?.appleUserId
|
|
53
56
|
};
|
|
54
57
|
if (route.params?.registrationToken) {
|
|
55
58
|
navigation.navigate('UserConsent', {
|
|
@@ -10,7 +10,7 @@ const useConsent = () => {
|
|
|
10
10
|
const [loading, setLoading] = useState(false);
|
|
11
11
|
const { registrationMethod } = useAuthPackageContext();
|
|
12
12
|
const onAgree = ({ userDetails, source, onResult }) => {
|
|
13
|
-
const { firstName, lastName, selectedDOB, gender, referralCode, email, phone, countryCode, password, registrationToken } = userDetails || {};
|
|
13
|
+
const { firstName, lastName, selectedDOB, gender, referralCode, email, phone, countryCode, password, registrationToken, fbUserId, googleUserId, appleUserId } = userDetails || {};
|
|
14
14
|
const payload = {
|
|
15
15
|
firstName,
|
|
16
16
|
lastName,
|
|
@@ -37,6 +37,15 @@ const useConsent = () => {
|
|
|
37
37
|
if (registrationToken) {
|
|
38
38
|
payload.ssoRegistrationToken = registrationToken;
|
|
39
39
|
}
|
|
40
|
+
if (googleUserId) {
|
|
41
|
+
payload.googleUserId = googleUserId;
|
|
42
|
+
}
|
|
43
|
+
if (fbUserId) {
|
|
44
|
+
payload.fbUserId = fbUserId;
|
|
45
|
+
}
|
|
46
|
+
if (appleUserId) {
|
|
47
|
+
payload.appleUserId = appleUserId;
|
|
48
|
+
}
|
|
40
49
|
setLoading(true);
|
|
41
50
|
axiosClient({
|
|
42
51
|
url: '/auth/registration/initiate',
|
|
@@ -15,7 +15,7 @@ const useVerifyEmail = ({ sessionToken }) => {
|
|
|
15
15
|
}, []);
|
|
16
16
|
const resendOTP = useCallback(() => {
|
|
17
17
|
if (!sessionTokenState) {
|
|
18
|
-
console.
|
|
18
|
+
console.log('useVerifyEmail: sessionToken is required to resend OTP');
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
setResendOTPCounter(prevCounter => prevCounter + 1);
|
|
@@ -33,7 +33,7 @@ const useVerifyEmail = ({ sessionToken }) => {
|
|
|
33
33
|
}, [sessionTokenState]);
|
|
34
34
|
const validateOTP = useCallback(({ otp, onVerifiedOTP }) => {
|
|
35
35
|
if (!sessionTokenState) {
|
|
36
|
-
console.
|
|
36
|
+
console.log('useVerifyEmail: sessionToken is required to resend OTP');
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
setStatus('loading');
|
package/build/src/screens/VerifyResetPasswordOTP/hooks/internal/useVerifyResetPasswordOTP.js
CHANGED
|
@@ -27,7 +27,7 @@ const useVerifyResetPasswordOTP = ({ email }) => {
|
|
|
27
27
|
setResendOTPCounter(prevCounter => prevCounter + 1);
|
|
28
28
|
sessionTokenRef.current = res.data.sessionToken;
|
|
29
29
|
}).catch((error) => {
|
|
30
|
-
console.
|
|
30
|
+
console.log('Failed to send OTP:', error);
|
|
31
31
|
const message = error?.response?.data?.errors?.[0]?.message || "Failed to send OTP. Please try again.";
|
|
32
32
|
showMessage({ message });
|
|
33
33
|
}).finally(() => setStatus("idle"));
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/screens/Welcome/SocialAuth/commonSocialAuth.ts
|
|
2
|
+
import { LoginMethodCode, SocialLoginsEnum } from "../../../enums";
|
|
3
|
+
import { axiosClient } from "../../../api/axiosClient";
|
|
4
|
+
import { checkEmailExists } from "../../../api/auth";
|
|
5
|
+
// ============================================
|
|
6
|
+
// TYPES & ENUMS
|
|
7
|
+
// ============================================
|
|
8
|
+
export var SocialAuthStatus;
|
|
9
|
+
(function (SocialAuthStatus) {
|
|
10
|
+
SocialAuthStatus["REGISTER"] = "REGISTER";
|
|
11
|
+
SocialAuthStatus["INVALID"] = "INVALID";
|
|
12
|
+
SocialAuthStatus["SUCCESS"] = "SUCCESS";
|
|
13
|
+
})(SocialAuthStatus || (SocialAuthStatus = {}));
|
|
14
|
+
// ============================================
|
|
15
|
+
// CONSTANTS
|
|
16
|
+
// ============================================
|
|
17
|
+
const EXISTING_LOGIN_TYPE = {
|
|
18
|
+
[LoginMethodCode.Email]: 'Email Address',
|
|
19
|
+
[LoginMethodCode.Facebook]: 'Facebook Account',
|
|
20
|
+
[LoginMethodCode.Google]: 'Google Account',
|
|
21
|
+
[LoginMethodCode.Apple]: 'Apple ID',
|
|
22
|
+
};
|
|
23
|
+
const PROVIDER_CONFIG = {
|
|
24
|
+
[SocialLoginsEnum.Google]: {
|
|
25
|
+
endpoint: '/auth/google',
|
|
26
|
+
tokenKey: 'token',
|
|
27
|
+
loginMethodCode: LoginMethodCode.Google,
|
|
28
|
+
},
|
|
29
|
+
[SocialLoginsEnum.Facebook]: {
|
|
30
|
+
endpoint: '/auth/facebook',
|
|
31
|
+
tokenKey: 'token',
|
|
32
|
+
loginMethodCode: LoginMethodCode.Facebook,
|
|
33
|
+
},
|
|
34
|
+
[SocialLoginsEnum.Apple]: {
|
|
35
|
+
endpoint: '/auth/apple/appleid-exists',
|
|
36
|
+
tokenKey: 'token',
|
|
37
|
+
loginMethodCode: LoginMethodCode.Apple,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
// ============================================
|
|
41
|
+
// HELPER FUNCTIONS
|
|
42
|
+
// ============================================
|
|
43
|
+
/**
|
|
44
|
+
* Verifies if the attempted login method matches the registered method for an email
|
|
45
|
+
* @param email - User's email address
|
|
46
|
+
* @param attemptedLoginCode - Login method code being attempted
|
|
47
|
+
* @returns Verification result with email existence and validity
|
|
48
|
+
*/
|
|
49
|
+
const verifyLoginType = async (email, attemptedLoginCode) => {
|
|
50
|
+
const { emailExist, loginType } = await checkEmailExists(email);
|
|
51
|
+
const existingLoginCode = Number(loginType);
|
|
52
|
+
if (emailExist) {
|
|
53
|
+
const isValidLoginType = EXISTING_LOGIN_TYPE[attemptedLoginCode] === EXISTING_LOGIN_TYPE[existingLoginCode];
|
|
54
|
+
return {
|
|
55
|
+
emailExist,
|
|
56
|
+
isValidLoginType,
|
|
57
|
+
existingLoginType: EXISTING_LOGIN_TYPE[existingLoginCode],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return { emailExist };
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Generic handler for social authentication
|
|
64
|
+
* @param provider - Social provider (google, facebook, apple)
|
|
65
|
+
* @param email - User's email
|
|
66
|
+
* @param identityToken - Provider's identity token
|
|
67
|
+
* @param platform - Platform (web, ios, android)
|
|
68
|
+
* @returns Authentication result
|
|
69
|
+
*/
|
|
70
|
+
const handleSocialAuth = async (provider, email, identityToken, platform) => {
|
|
71
|
+
try {
|
|
72
|
+
const config = PROVIDER_CONFIG[provider];
|
|
73
|
+
// Verify login type
|
|
74
|
+
const { emailExist, isValidLoginType, existingLoginType } = await verifyLoginType(email, config.loginMethodCode);
|
|
75
|
+
// New user - needs registration
|
|
76
|
+
if (!emailExist) {
|
|
77
|
+
return {
|
|
78
|
+
status: SocialAuthStatus.REGISTER,
|
|
79
|
+
data: { email },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// Wrong Social login method
|
|
83
|
+
if (!isValidLoginType) {
|
|
84
|
+
return {
|
|
85
|
+
status: SocialAuthStatus.INVALID,
|
|
86
|
+
data: { existingLoginType },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Authenticate with backend
|
|
90
|
+
const { data } = await axiosClient.post(config.endpoint, {
|
|
91
|
+
[config.tokenKey]: identityToken,
|
|
92
|
+
source: platform,
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
status: SocialAuthStatus.SUCCESS,
|
|
96
|
+
data: {
|
|
97
|
+
token: data.token,
|
|
98
|
+
member: data.member,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Let the calling code handle the error
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
// ============================================
|
|
108
|
+
// PUBLIC API
|
|
109
|
+
// ============================================
|
|
110
|
+
/**
|
|
111
|
+
* Handles Google authentication
|
|
112
|
+
*/
|
|
113
|
+
export const handleGoogleAuth = async ({ email, googleIdentityToken, platform, }) => {
|
|
114
|
+
return handleSocialAuth(SocialLoginsEnum.Google, email, googleIdentityToken, platform);
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Handles Facebook authentication
|
|
118
|
+
*/
|
|
119
|
+
export const handleFacebookAuth = async ({ email, facebookIdentityToken, platform }) => {
|
|
120
|
+
return handleSocialAuth(SocialLoginsEnum.Facebook, email, facebookIdentityToken, platform);
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Handles Apple authentication
|
|
124
|
+
*/
|
|
125
|
+
export const handleAppleAuth = async ({ email, appleIdentityToken, platform }) => {
|
|
126
|
+
return handleSocialAuth(SocialLoginsEnum.Apple, email, appleIdentityToken, platform);
|
|
127
|
+
};
|