跳到主要内容
about

The Authentication module provides a complete user authentication flow including login, signup, password management, and third party login. It uses the CDF (Central Data Framework) for handling authentication operations.

Authentication is implemented using ESP RainMaker's user management module with support for email based authentication and also integration with third party social logins like Gmail and Apple.

  • To know more about enabling third party login, click here.

Authentication

This guide explains how the Authentication module works, focusing on the major operations and how CDF (Central Data Framework) APIs are used throughout the system.

User Flow Overview

The Authentication module has five main user flows:

  1. New User Registration: Login → Signup → Code Verification → Auto Login → Home
  2. Existing User Login: Login → Home
  3. Third Party Login: Login → Third Party Provider → Success → Home
  4. Password Reset: Login → Forgot Password → Reset Password (OTP + New Password) → Login
  5. Change Password: Home → Profile → Account & Security → Change Password → Success → Auto Logout → Login

Architecture Overview

Flow Explanation:

  1. Entry Point: User starts at Login.tsx
  2. Authentication Module: Login, Signup, or Third Party options
  3. Third Party Flow: User clicks provider icon → Redirects to provider → Returns on success
  4. Signup Verification: Signup → Code verification (ConfirmationCode.tsx) → Auto login → Home
  5. Password Reset Flow: Forgot Password → Reset Password (OTP + New Password in single screen) → Login
  6. Success: Redirect to home screen or back to login
  7. Change Password: From home screen → Profile → Account & Security → Change password → Auto logout → Back to login

Components Structure

app/(auth)/
├── Login.tsx # Main login screen
├── Signup.tsx # User registration
├── ConfirmationCode.tsx # Code verification (for signup)
├── Forgot.tsx # Password reset request
├── ResetPassword.tsx # Password reset with OTP and new password
└── ChangePassword.tsx # Password change
Terms and Privacy

Terms of Use and Privacy Policy are shown in the signup consent section. The URLs are configured in utils/constants.ts via TERMS_OF_USE_LINK and PRIVACY_POLICY_LINK. The consent checkbox, link text, and handlers that open these URLs in a browser are implemented in app/(auth)/Signup.tsx (see showTerms and showPrivacy).

1. Login Screen (Login.tsx)

The main authentication module screen where users can sign in.

CDF Store

// Get CDF stores
const { store } = useCDF();
const { userStore } = store;

// Access auth instance
const authInstance = store.userStore.authInstance;

Login Operations

// Get CDF stores and hooks
const { store, fetchNodesAndGroups, initUserCustomData, refreshESPRMUser } = useCDF();
const { t } = useTranslation();
const router = useRouter();
const toast = useToast();

// Email/Password login
const login = async () => {
// Check if form is valid before submitting
if (!isEmailValid || !isPasswordValid) {
return;
}

setIsLoading(true);

try {
const res = await store.userStore.login(email, password);
if (!res) {
return;
}

await executePostLoginPipeline({
store,
router,
refreshESPRMUser,
fetchNodesAndGroups,
initUserCustomData,
});
} catch (error: any) {
toast.showError(
t("auth.errors.signInFailed"),
error?.description || t("auth.errors.fallback")
);
} finally {
// reset loading state
setIsLoading(false);
}
};

2. Third Party Login Flow

The authentication module handles login through external providers like Google and Apple.

User Flow Steps

// Get OAuth providers configuration
const ENABLED_OAUTH_PROVIDERS = Constants.expoConfig?.extra?.enabledThirdPartyProviders || [];
const OAUTH_PROVIDER_IMAGES = {
google: google,
signinwithapple: signinwithapple,
} as Record<string, ImageSourcePropType>;

// 1. User clicks third party provider icon
const oauthLogin = async (provider: string) => {
setIsOAuthLoading(true);
setPipelineProgress(null);
try {
const authInstance = store.userStore.authInstance;
if (!authInstance) {
throw new Error("Auth instance not found");
}

// 2. Redirect to provider's authentication page
const userInstance = await authInstance.loginWithOauth(provider);

store.userStore[CDF_EXTERNAL_PROPERTIES.IS_OAUTH_LOGIN] = true;
await store.userStore.setUserInstance(userInstance);

// 3. On success, execute post-login pipeline
if (userInstance) {
// Initialize pipeline progress tracking
const pipelineSteps = [
{ name: "refreshESPRMUser", status: "pending" as const },
{ name: "setUserTimeZone", status: "pending" as const },
{ name: "createPlatformEndpoint", status: "pending" as const },
{ name: "fetchNodesAndGroups", status: "pending" as const },
{ name: "updateRefreshTokensForAllAIDevices", status: "pending" as const },
{ name: "initUserCustomData", status: "pending" as const },
{ name: "getUserProfileAndRoute", status: "pending" as const },
];

setPipelineProgress({
currentStep: "",
completed: 0,
total: pipelineSteps.length,
steps: pipelineSteps,
});

await executePostLoginPipeline({
store,
router,
refreshESPRMUser,
fetchNodesAndGroups,
initUserCustomData,
onStepStart: (stepName) => {
setPipelineProgress((prev) => {
if (!prev) return prev;
return {
...prev,
currentStep: stepName,
steps: prev.steps.map((step) =>
step.name === stepName ? { ...step, status: "running" as const } : step
),
};
});
},
onStepComplete: (stepName) => {
setPipelineProgress((prev) => {
if (!prev) return prev;
const updatedSteps = prev.steps.map((step) =>
step.name === stepName ? { ...step, status: "completed" as const } : step
);
const completed = updatedSteps.filter((s) => s.status === "completed").length;
return {
...prev,
currentStep: stepName,
completed,
steps: updatedSteps,
};
});
},
onProgress: (stepName, state) => {
setPipelineProgress((prev) => {
if (!prev) return prev;
return {
...prev,
currentStep: stepName,
completed: state.completed,
total: state.total,
};
});
},
});
}
} catch (error) {
console.error(`OAuth login failed for provider ${provider}:`, error);

// Handle different types of OAuth errors
let errorMessage = "OAuth login failed. Please try again.";

if (error instanceof Error) {
if (error.message.includes("OAUTH_CANCELLED")) {
errorMessage = "OAuth login was cancelled.";
} else if (error.message.includes("NO_BROWSER_FOUND")) {
errorMessage = "No browser app found. Please install a browser.";
} else {
errorMessage = `OAuth error: ${error.message}`;
}
}

// Show error toast to user
toast.showError("OAuth Login Failed", errorMessage);
setPipelineProgress(null);
} finally {
// Hide loader when login completes (success or failure)
setIsOAuthLoading(false);
}
};

Provider Icons Implementation

// Configuration for enabled OAuth providers
const ENABLED_OAUTH_PROVIDERS = Constants.expoConfig?.extra?.enabledThirdPartyProviders || [];
const OAUTH_PROVIDER_IMAGES = {
google: google,
signinwithapple: signinwithapple,
} as Record<string, ImageSourcePropType>;

// Third party provider buttons
{ENABLED_OAUTH_PROVIDERS.length > 0 && (
<>
<Text {...testProps("text_3plogin")} style={globalStyles.thirdLoginText}>
{t("auth.login.thirdPartyLogin")}
</Text>
<View {...testProps("view_3plogin")} style={globalStyles.oauthContainer}>
{/* Enabled OAuth Providers */}
{ENABLED_OAUTH_PROVIDERS.map((provider) => (
<TouchableOpacity
key={provider}
onPress={() => oauthLogin(provider)}
style={globalStyles.oauthButton}
{...testProps(`button_3p_${provider}`)}
>
<Image
{...testProps(`image_3p_${provider}`)}
source={OAUTH_PROVIDER_IMAGES[provider.toLocaleLowerCase()]}
style={globalStyles.oauthImage}
/>
</TouchableOpacity>
))}
</View>
</>
)}

What this does:

  • Checks if OAuth providers are enabled via Constants.expoConfig?.extra?.enabledThirdPartyProviders
  • User clicks on third party provider icon (Google, Apple, etc.)
  • App redirects to the provider's authentication page via authInstance.loginWithOauth()
  • Provider handles authentication and returns user instance
  • Sets CDF_EXTERNAL_PROPERTIES.IS_OAUTH_LOGIN flag to true
  • User instance is stored in CDF via store.userStore.setUserInstance()
  • On success, executes post-login pipeline with progress tracking
  • Shows loading screen with progress indicators during pipeline execution
  • Tracks pipeline steps (refreshESPRMUser, setUserTimeZone, createPlatformEndpoint, etc.)
  • Displays friendly step names to user during pipeline execution
  • Handles cancellation, browser-related errors, and other OAuth errors
  • Shows appropriate error messages via toast notifications
  • Provides cancel button to abort OAuth login process

3. Signup Screen (Signup.tsx)

Handles new user registration with email verification.

// Get CDF stores and hooks
const { store } = useCDF();
const { t } = useTranslation();
const router = useRouter();
const toast = useToast();

// Form state
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [isEmailValid, setIsEmailValid] = useState(false);
const [isPasswordValid, setIsPasswordValid] = useState(false);
const [isConfirmPasswordValid, setIsConfirmPasswordValid] = useState(false);
const [consentChecked, setConsentChecked] = useState(false);
const [isLoading, setIsLoading] = useState(false);

// Email validator
const emailValidator = (email: string): { isValid: boolean; error?: string } => {
if (!email.trim()) {
return { isValid: false };
}
if (!validateEmail(email)) {
return { isValid: false, error: t("auth.validation.invalidEmail") };
}
return { isValid: true };
};

// Password validator
const passwordValidator = (password: string): { isValid: boolean; error?: string } => {
if (!password.trim()) {
return { isValid: false };
}
return { isValid: true };
};

// Confirm password validator
const confirmPasswordValidator = (confirmPwd: string): { isValid: boolean; error?: string } => {
if (!confirmPwd.trim()) {
return { isValid: false };
}
if (confirmPwd !== password) {
return {
isValid: false,
error: t("auth.validation.passwordsDoNotMatch"),
};
}
return { isValid: true };
};

// Send signup verification code
const signup = () => {
// Check if all fields are valid before submitting
if (
!isEmailValid ||
!isPasswordValid ||
!isConfirmPasswordValid ||
!consentChecked
) {
return;
}

setIsLoading(true);

// send sign up code
store.userStore.authInstance
?.sendSignUpCode(email, password)
.then((res) => {
if (res.status === "success") {
// redirect to code screen
router.push({
pathname: "/(auth)/ConfirmationCode",
params: {
email: email,
password: password,
},
});
} else {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
res.description
);
}
})
.catch((error) => {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
error.description || t("auth.errors.fallback")
);
})
.finally(() => {
// reset loading state
setIsLoading(false);
});
};

What this does:

  • Validates user input (email, password, confirm password)
  • Requires consent checkbox to be checked (Terms of Use and Privacy Policy)
  • Sends verification code via CDF auth instance
  • Navigates to code verification screen with email and password params
  • Handles errors and loading states

4. Confirmation Code Screen (ConfirmationCode.tsx)

Handles verification codes for new user signup only. Password reset uses ResetPassword.tsx instead.

Code Verification

// Get CDF stores and params
const { store, initUserCustomData, refreshESPRMUser, fetchNodesAndGroups } = useCDF();
const { email, password = "" } = useLocalSearchParams();
const toast = useToast();

const [code, setCode] = useState("");
const [isCodeValid, setIsCodeValid] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [countdown, setCountdown] = useState(0);

// Code validator
const codeValidator = (inputCode: string): { isValid: boolean; error?: string } => {
if (!inputCode.trim()) {
return { isValid: false };
}
if (inputCode.trim().length !== 6) {
return {
isValid: false,
error: t("auth.validation.invalidCode"),
};
}
return { isValid: true };
};

// Handle code verification for signup
const handleVerify = () => {
// Check if the code is valid before submitting
if (!isCodeValid || !code.trim()) {
return;
}

setIsLoading(true);

// Confirm signup
store.userStore.authInstance
?.confirmSignUp(email as string, code)
.then(async (res) => {
if (res.status === "success") {
toast.showSuccess(t("auth.signup.registrationSuccess"));
// Auto-login after successful signup verification
await loginUser();
} else {
toast.showError(
t("auth.errors.signupConfirmationFailed"),
res.description || t("auth.errors.fallback")
);
}
})
.catch((error) => {
toast.showError(
t("auth.errors.signupConfirmationFailed"),
error.description || t("auth.errors.fallback")
);
})
.finally(() => {
setIsLoading(false);
});
};

// Auto-authenticates the user after signup verification
const loginUser = async () => {
try {
const res = await store.userStore.login(email as string, password as string);
if (res) {
router.dismissAll();
await executePostLoginPipeline({
store,
router,
refreshESPRMUser,
fetchNodesAndGroups,
initUserCustomData,
});
}
} catch (error: any) {
toast.showError(
t("auth.errors.autoSignInFailed"),
error.description || t("auth.errors.fallback")
);
setTimeout(() => {
router.dismissTo({
pathname: "/(auth)/Login",
params: { email: email },
});
}, 10000);
}
};

Resend Code Functionality

// Resend verification code for signup
const handleResendCode = () => {
if (countdown > 0) return;

setIsLoading(true);

// Send verification code for signup
store.userStore.authInstance
?.sendSignUpCode(email as string, password as string)
.then((res) => {
if (res.status === "success") {
toast.showSuccess(t("auth.verification.heading"));
// reset countdown to 60 seconds
setCountdown(60);
} else {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
res.description || t("auth.errors.fallback")
);
}
})
.catch((error) => {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
error.description || t("auth.errors.fallback")
);
})
.finally(() => {
// reset loading state
setIsLoading(false);
});
};

What this does:

  • Handles signup verification code entry (6-digit code)
  • Uses CDF auth instance confirmSignUp method for verification
  • Provides resend functionality with 60-second countdown timer
  • Auto-login after successful signup verification using executePostLoginPipeline
  • Redirects to home screen after successful verification via post-login pipeline
  • Handles auto-login failures with error toast and redirects to login screen after 10 seconds

5. Forgot Password Screen (Forgot.tsx)

Handles password reset request for existing users. This is the initial step where users enter their email to receive a verification code.

// Get CDF stores and hooks
const { t } = useTranslation();
const { store } = useCDF();
const router = useRouter();
const toast = useToast();

// Form state
const [email, setEmail] = useState("");
const [isEmailValid, setIsEmailValid] = useState(false);
const [isLoading, setIsLoading] = useState(false);

// Email validator
const emailValidator = (email: string): { isValid: boolean; error?: string } => {
if (!email.trim()) {
return { isValid: false };
}
if (!validateEmail(email)) {
return { isValid: false, error: t("auth.validation.invalidEmail") };
}
return { isValid: true };
};

// Handle email change
const handleEmailChange = (value: string, isValid: boolean) => {
setEmail(value.trim());
setIsEmailValid(isValid);
};

// Sends verification code to user's email for password reset
const sendVerificationCode = () => {
// Check if email is valid before submitting
if (!isEmailValid || !email) {
return;
}

setIsLoading(true);

// send verification code to user's email address
store.userStore.authInstance
?.forgotPassword(email)
.then((res) => {
// if success, redirect to reset password screen
if (res.status === "success") {
toast.showSuccess(t("auth.verification.heading"));
router.push({
pathname: "/(auth)/ResetPassword",
params: {
email: email,
},
});
} else {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
res.description || t("auth.errors.fallback")
);
}
})
.catch((error) => {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
error.description || t("auth.errors.fallback")
);
})
.finally(() => {
setIsLoading(false);
});
};

What this does:

  • Collects user email address with validation
  • Sends password reset verification code via CDF auth instance forgotPassword method
  • Shows success toast when code is sent
  • Navigates to ResetPassword.tsx screen with email param
  • Handles validation and error states

6. Reset Password Screen (ResetPassword.tsx)

Handles password reset with OTP verification. This screen combines OTP verification and new password entry into a single step.

// Get CDF stores and params
const { t } = useTranslation();
const { store } = useCDF();
const { email } = useLocalSearchParams();
const toast = useToast();

// Form state
const [code, setCode] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [isCodeValid, setIsCodeValid] = useState(false);
const [isPasswordValid, setIsPasswordValid] = useState(false);
const [isConfirmPasswordValid, setIsConfirmPasswordValid] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [countdown, setCountdown] = useState(0);

// Code validator - checks if code is 6 digits
const codeValidator = (inputCode: string): { isValid: boolean; error?: string } => {
if (!inputCode.trim()) {
return { isValid: false };
}
if (inputCode.trim().length !== 6) {
return {
isValid: false,
error: t("auth.validation.invalidCode"),
};
}
return { isValid: true };
};

// Password validator
const passwordValidator = (password: string): { isValid: boolean; error?: string } => {
if (!password.trim()) {
return { isValid: false };
}
return { isValid: true };
};

// Confirm password validator - checks if passwords match
const confirmPasswordValidator = useCallback(
(confirmPwd: string): { isValid: boolean; error?: string } => {
if (!confirmPwd.trim()) {
return { isValid: false };
}
if (confirmPwd !== newPassword) {
return {
isValid: false,
error: t("auth.validation.passwordsDoNotMatch"),
};
}
return { isValid: true };
},
[newPassword, t]
);

// Handle password change - re-validates confirm password when new password changes
const handlePasswordChange = (value: string, isValid: boolean) => {
const newPwd = value.trim();
setNewPassword(newPwd);
setIsPasswordValid(isValid);

// Re-validate confirm password when new password changes
if (confirmPassword.trim()) {
const isConfirmValid = confirmPassword.trim() === newPwd;
setIsConfirmPasswordValid(isConfirmValid);
}
};

// Handle password reset with OTP verification
const handleResetPassword = () => {
// Check if all fields are valid before submitting
if (
!isCodeValid ||
!code.trim() ||
!isPasswordValid ||
!newPassword.trim() ||
!isConfirmPasswordValid ||
!confirmPassword.trim()
) {
return;
}

setIsLoading(true);

store.userStore.authInstance
?.setNewPassword(email as string, newPassword, code)
.then((res) => {
if (res.status === "success") {
toast.showSuccess(t("auth.forgotPassword.resetSuccess"));
// redirect to login page
router.dismissTo({
pathname: "/(auth)/Login",
params: { email: email },
});
} else {
toast.showError(
t("auth.errors.passwordResetFailed"),
res.description || t("auth.errors.fallback")
);
}
})
.catch((error) => {
toast.showError(
t("auth.errors.passwordResetFailed"),
error.description || t("auth.errors.fallback")
);
})
.finally(() => {
setIsLoading(false);
});
};

// Resend verification code
const handleResendCode = () => {
if (countdown > 0) return;

setIsLoading(true);

// Send verification code for password reset
store.userStore.authInstance
?.forgotPassword(email as string)
.then((res) => {
if (res.status === "success") {
toast.showSuccess(t("auth.verification.heading"));
// reset countdown to 60 seconds
setCountdown(60);
} else {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
res.description || t("auth.errors.fallback")
);
}
})
.catch((error) => {
toast.showError(
t("auth.errors.verificationCodeSendFailed"),
error.description || t("auth.errors.fallback")
);
})
.finally(() => {
// reset loading state
setIsLoading(false);
});
};

What this does:

  • Displays 6-digit OTP code input field (auto-focused, numeric keyboard)
  • Collects new password and confirm password with real-time validation
  • Validates password matching in real-time using confirmPasswordValidator
  • Re-validates confirm password when new password changes
  • Verifies OTP code and sets new password via CDF auth instance setNewPassword method
  • Provides resend code functionality with 60-second countdown timer
  • Shows success toast and redirects to login screen after successful password reset
  • Passes email param to login screen for pre-filling
  • Handles validation and error states

7. Change Password Screen (ChangePassword.tsx)

Allows logged-in users to change their password.

备注

This feature is only available for users who signed up using email/password authentication. Users who logged in through third-party providers (Google, Apple) cannot change their password through the app as their authentication is managed by the respective providers.

// Get CDF stores and hooks
const { store } = useCDF();
const { t } = useTranslation();
const toast = useToast();
const router = useRouter();

// Form state
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [isOldPasswordValid, setIsOldPasswordValid] = useState(false);
const [isNewPasswordValid, setIsNewPasswordValid] = useState(false);
const [isConfirmPasswordValid, setIsConfirmPasswordValid] = useState(false);
const [isLoading, setIsLoading] = useState(false);

// Old password validator
const oldPasswordValidator = (password: string): { isValid: boolean; error?: string } => {
if (!password.trim()) {
return { isValid: false };
}
return { isValid: true };
};

// New password validator - checks if new password is same as old password
const newPasswordValidator = (password: string): { isValid: boolean; error?: string } => {
if (!password.trim()) {
return { isValid: false };
}
// Check if new password is same as old password
if (password === oldPassword) {
return {
isValid: false,
error: t("auth.validation.newPasswordSameAsCurrent"),
};
}
return { isValid: true };
};

// Confirm password validator
const confirmPasswordValidator = useCallback(
(confirmPwd: string): { isValid: boolean; error?: string } => {
if (!confirmPwd.trim()) {
return { isValid: false };
}
if (confirmPwd !== newPassword) {
return {
isValid: false,
error: t("auth.validation.passwordsDoNotMatch"),
};
}
return { isValid: true };
},
[newPassword, t]
);

// Handle password change submission
const handleSubmit = async () => {
// Check if all fields are valid before submitting
if (
!isOldPasswordValid ||
!isNewPasswordValid ||
!isConfirmPasswordValid ||
!oldPassword ||
!newPassword ||
!confirmPassword
) {
return;
}

setIsLoading(true);

try {
await store.userStore.user?.changePassword(oldPassword, newPassword);
toast.showSuccess(t("auth.changePassword.passwordChangedSuccessfully"));
// Logout user
await store.userStore.user?.logout();
router.replace("/(auth)/Login");
} catch (error) {
toast.showError(
t("auth.errors.changePasswordFailed"),
(error as ESPAPIError).description || t("auth.errors.fallback")
);
} finally {
// Reset loading state
setIsLoading(false);
}
};

What this does:

  • Validates old password, new password, and confirm password
  • Prevents using the same password as the current password
  • Validates password matching in real-time
  • Uses CDF user instance changePassword method to change password
  • Automatically logs out user after successful password change
  • Redirects to login screen after logout
  • Handles errors and loading states

Post-Login Pipeline

After a successful login (email/password or third party), the app runs a post-login pipeline defined in utils/postLoginPipeline.ts (executePostLoginPipeline). It runs steps in order with dependencies and optional/background steps:

  1. refreshESPRMUser — Refresh the ESP RainMaker user instance.
  2. setUserTimeZone (optional, background) — Set user timezone.
  3. createPlatformEndpoint (optional, background) — Create platform endpoint for push notifications.
  4. fetchNodesAndGroups (unless skipNodesFetch) — Fetch nodes and groups from CDF.
  5. updateRefreshTokensForAllAIDevices (optional, background) — Update refresh tokens for AI devices.
  6. initUserCustomData — Initialize user custom data.
  7. autoConnectMCPConnector (optional, background) — Auto-connects MCP RainMaker connector for default agent.
  8. getUserProfileAndRoute — Load user profile and navigate to /(group)/Home.

Step dependencies

StepDepends on
refreshESPRMUser
setUserTimeZonerefreshESPRMUser
createPlatformEndpointrefreshESPRMUser
fetchNodesAndGroupsrefreshESPRMUser
updateRefreshTokensForAllAIDevicesfetchNodesAndGroups
initUserCustomDatafetchNodesAndGroups (when skipNodesFetch is false) or refreshESPRMUser (when skipNodesFetch is true)
autoConnectMCPConnectorinitUserCustomData
getUserProfileAndRouteinitUserCustomData

The pipeline uses utils/pipelineTask.ts for step ordering, dependsOn, and optional/background execution. Progress and step callbacks can be passed via onProgress, onStepStart, and onStepComplete.

CDF API Usage Summary

1. User Store Operations

// Get user store
const { store } = useCDF();
const { userStore } = store;

// Login
await userStore.login(email, password);

// Get auth instance
const authInstance = userStore.authInstance;

// Get user instance
const user = userStore.user;

2. Auth Instance Operations

// Signup
await authInstance.sendSignUpCode(email, password);
await authInstance.confirmSignUp(email, code);

// Password reset
await authInstance.forgotPassword(email);
await authInstance.setNewPassword(email, password, code);

// Third party login
const userInstance = await authInstance.loginWithOauth(provider);
store.userStore.setUserInstance(userInstance);

3. User Instance Operations

// Password change
await user.changePassword(oldPassword, newPassword);

// Logout
await user.logout();

// Get user info
const userInfo = await user.getUserInfo();

Data Flow Examples

Signup Flow Example

// 1. User enters signup data in Signup.tsx
const email = "user@example.com";
const password = "password123";
const confirmPassword = "password123";
const consentChecked = true; // Terms of Use and Privacy Policy consent

// 2. Validate inputs and send verification code via CDF
await store.userStore.authInstance.sendSignUpCode(email, password);

// 3. Navigate to ConfirmationCode.tsx
router.push({
pathname: "/(auth)/ConfirmationCode",
params: {
email: email,
password: password,
},
});

// 4. User enters verification code
const code = "123456";

// 5. Confirm signup via CDF
await store.userStore.authInstance.confirmSignUp(email, code);

// 6. Auto-login after successful signup verification
await store.userStore.login(email, password);

// 7. Execute post-login pipeline (includes redirect to home)
await executePostLoginPipeline({
store,
router,
refreshESPRMUser,
fetchNodesAndGroups,
initUserCustomData,
});

Password Reset Flow Example

// 1. User enters email in Forgot.tsx
const email = "user@example.com";

// 2. Send verification code via CDF
await store.userStore.authInstance.forgotPassword(email);

// 3. Navigate to ResetPassword.tsx
router.push({
pathname: "/(auth)/ResetPassword",
params: { email: email },
});

// 4. User enters OTP code and new password in ResetPassword.tsx
const code = "123456";
const newPassword = "newpassword123";

// 5. Set new password via CDF
await store.userStore.authInstance.setNewPassword(email, newPassword, code);

// 6. Redirect back to login
router.dismissTo({
pathname: "/(auth)/Login",
params: { email: email },
});

Third Party Login Flow Example

// 1. Check if OAuth providers are enabled
const ENABLED_OAUTH_PROVIDERS = Constants.expoConfig?.extra?.enabledThirdPartyProviders || [];

// 2. User clicks third party provider icon
const provider = 'google';

// 3. Redirect to provider's authentication page
const authInstance = store.userStore.authInstance;
const userInstance = await authInstance.loginWithOauth(provider);

// 4. Set OAuth login flag and store user instance in CDF
store.userStore[CDF_EXTERNAL_PROPERTIES.IS_OAUTH_LOGIN] = true;
await store.userStore.setUserInstance(userInstance);

// 5. Execute post-login pipeline with progress tracking (includes redirect to home)
await executePostLoginPipeline({
store,
router,
refreshESPRMUser,
fetchNodesAndGroups,
initUserCustomData,
onStepStart: (stepName) => {
// Track step start
},
onStepComplete: (stepName) => {
// Track step completion
},
onProgress: (stepName, state) => {
// Track overall progress
},
});

Key Takeaways

  1. CDF is the Backbone: All authentication module operations use CDF UserStore apis
  2. Two Main Flows: Signup (with verification) vs Login (direct)
  3. Code Verification: Required for signup and password reset
  4. Third Party Support: Configurable third party providers
  5. Auto-logout: Password changes trigger automatic logout
  6. Error Handling: Always wrap CDF operations in try-catch blocks
  7. Loading States: Show loading indicators during async operations
  8. Change Password: Only available for email/password users, not for third-party login users
  9. Forgot Password: Available only for email/password users, requires email verification code
  10. Reset Password: Combines OTP verification and new password entry in a single screen for better UX

Common Patterns

// Pattern 1: CDF store access
const { store } = useCDF();
const { userStore } = store;

// Pattern 2: Async CDF operation with loading
const [isLoading, setIsLoading] = useState(false);
try {
setIsLoading(true);
await store.userStore.operation();
} catch (error) {
console.error(error);
toast.showError("Operation failed");
} finally {
setIsLoading(false);
}

// Pattern 3: Navigation with params
router.push({
pathname: "/(auth)/ConfirmationCode",
params: { email, type, password }
});

// Pattern 4: Form validation
const isFormValid = isEmailValid && isPasswordValid && isConfirmPasswordValid;

Error Handling Best Practices

// Always handle CDF errors
try {
const result = await store.userStore.operation();
if (result.status === "success") {
// Handle success
} else {
// Handle API error
toast.showError(result.description || "Operation failed");
}
} catch (error) {
// Handle network/system errors
console.error("Operation failed:", error);
toast.showError("Operation failed", error.description || "Unknown error");
} finally {
// Always reset loading state
setIsLoading(false);
}

Loading State Management

// Use loading state for all async operations
const [isLoading, setIsLoading] = useState(false);

// Disable buttons during loading
<Button
disabled={!isFormValid || isLoading}
isLoading={isLoading}
onPress={handleSubmit}
/>

// Show loading in UI
{isLoading && <ActivityIndicator size="small" color={tokens.colors.primary} />}