Skip to content

Conversation

@jaypanchal-13
Copy link
Contributor

@jaypanchal-13 jaypanchal-13 commented May 23, 2025

Technical Summary

Added api call for validating firebase id token using validate_firebase_id_token endpoints in PersonalIdPhoneVerificationFragment also added firebase otp implmentation in this PR
https://dimagi.atlassian.net/browse/CCCT-1159

Feature Flag

new api added

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

@coderabbitai
Copy link

coderabbitai bot commented May 23, 2025

📝 Walkthrough

Walkthrough

The changes introduce Firebase-based OTP authentication to the application. The app/build.gradle file is updated to include the Firebase Authentication library. New classes and interfaces are added to encapsulate OTP operations: OtpAuthService, OtpManager, OtpVerificationCallback, and FirebaseAuthService. The PersonalIdPhoneVerificationFragment is updated to use these components, enabling phone number verification via Firebase and backend validation of the resulting ID token. Supporting API endpoint constants and service methods are added to facilitate backend token validation. Input validation, callback handling, and user feedback mechanisms are integrated throughout the OTP workflow.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Fragment as PersonalIdPhoneVerificationFragment
    participant OtpManager
    participant FirebaseAuthService
    participant Firebase
    participant BackendAPI

    User->>Fragment: Request OTP
    Fragment->>OtpManager: requestOtp(phoneNumber)
    OtpManager->>FirebaseAuthService: requestOtp(phoneNumber)
    FirebaseAuthService->>Firebase: Send OTP
    Firebase-->>FirebaseAuthService: onCodeSent(verificationId)
    FirebaseAuthService-->>Fragment: onCodeSent(verificationId)

    User->>Fragment: Enter OTP and submit
    Fragment->>OtpManager: submitOtp(otpCode)
    OtpManager->>FirebaseAuthService: verifyOtp(otpCode)
    FirebaseAuthService->>Firebase: Verify OTP
    Firebase-->>FirebaseAuthService: onSuccess(FirebaseUser)
    FirebaseAuthService-->>Fragment: onSuccess(FirebaseUser)

    Fragment->>FirebaseUser: getIdToken()
    FirebaseUser-->>Fragment: firebaseIdToken
    Fragment->>BackendAPI: validateFirebaseIdToken(firebaseIdToken)
    BackendAPI-->>Fragment: Validation result
    Fragment-->>User: Show result / Navigate next
Loading

Suggested labels

skip-integration-tests

Suggested reviewers

  • pm-dimagi
  • shubham1g5
  • OrangeAndGreen

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🔭 Outside diff range comments (1)
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (1)

75-82: ⚠️ Potential issue

Missing state restoration for verificationId.

The verificationId field is not saved/restored during configuration changes. If the activity is recreated (e.g., screen rotation) after an OTP is sent but before verification, the user won't be able to verify the OTP as verificationId will be null.

Add state persistence for verificationId:

 @Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     if (savedInstanceState != null) {
         primaryPhone = savedInstanceState.getString(KEY_PHONE);
+        verificationId = savedInstanceState.getString("KEY_VERIFICATION_ID");
     }
     initOtpComponents();
 }

Also update onSaveInstanceState:

 @Override
 public void onSaveInstanceState(@NonNull Bundle outState) {
     super.onSaveInstanceState(outState);
     outState.putString(KEY_PHONE, primaryPhone);
+    if (verificationId != null) {
+        outState.putString("KEY_VERIFICATION_ID", verificationId);
+    }
 }
🧹 Nitpick comments (4)
app/src/org/commcare/connect/network/connectId/ApiService.java (1)

14-15: Consider adding parameter documentation.

The method implementation follows established patterns correctly. However, consider adding JavaDoc to document the expected structure of the firebaseIdToken map parameter (e.g., what keys are required).

Example:

+    /**
+     * Validates a Firebase ID token with the backend service.
+     * @param firebaseIdToken Map containing the Firebase ID token (e.g., {"id_token": "token_value"})
+     * @return Call for the validation response
+     */
     @POST(ApiEndPoints.validateFirebaseIdToken)
     Call<ResponseBody> validateFirebaseIdToken(@Body Map<String, String> firebaseIdToken);
app/src/org/commcare/utils/OtpAuthService.java (1)

1-25: Well-designed interface with room for improvement.

The interface follows good design principles with clear documentation. However, consider enhancing it with:

  1. Error handling specification: How should implementations communicate failures?
  2. Input validation guidelines: What constitutes a valid phone number format?
  3. Async behavior clarification: Are these operations synchronous or asynchronous?

Consider adding more specific documentation:

 /**
  * Interface defining the contract for OTP-based authentication operations.
  * <p>
  * Implementations of this interface are responsible for sending OTP codes to users
- * and verifying the OTP codes they enter.
+ * and verifying the OTP codes they enter. All operations are asynchronous and
+ * results are communicated via callback mechanisms defined by the implementation.
  */
 public interface OtpAuthService {

     /**
      * Sends an OTP to the specified phone number.
      *
-     * @param phoneNumber The recipient's phone number in a valid format
+     * @param phoneNumber The recipient's phone number in E.164 format (e.g., +1234567890)
+     * @throws IllegalArgumentException if phoneNumber is invalid
      */
     void requestOtp(String phoneNumber);

     /**
      * Verifies the OTP code entered by the user.
      *
-     * @param code The OTP code to verify
+     * @param code The OTP code to verify (typically 6 digits)
+     * @throws IllegalArgumentException if code format is invalid
      */
     void verifyOtp(String code);
app/src/org/commcare/utils/FirebaseAuthService.java (1)

78-87: Improve verification ID validation.

The current validation only checks for null or empty, but should also validate the format and provide better error context.

Apply this diff to enhance validation:

 public void verifyOtp(String code) {
-    if (verificationId == null || verificationId.isEmpty()) {
-        callback.onFailure("Missing verification ID");
+    if (verificationId == null || verificationId.trim().isEmpty()) {
+        callback.onFailure("Missing verification ID. Please request OTP first.");
         return;
     }
+    
+    if (code == null || code.trim().isEmpty()) {
+        callback.onFailure("OTP code cannot be empty");
+        return;
+    }
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (1)

162-166: Consider validating phone number format.

The phone number from arguments is used directly without format validation. Invalid formats could cause Firebase authentication to fail with unclear error messages.

Add phone number validation in getPhoneNumberFromArguments:

 private void getPhoneNumberFromArguments() {
     if (getArguments() != null) {
         primaryPhone = PersonalIdPhoneVerificationFragmentArgs.fromBundle(getArguments()).getPrimaryPhone();
+        // Validate phone number format (e.g., starts with +, contains only digits)
+        if (primaryPhone != null && !primaryPhone.matches("^\\+[1-9]\\d{1,14}$")) {
+            Logger.log("WARN", "Invalid phone number format: " + primaryPhone);
+        }
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89fc8ee and f4361df.

📒 Files selected for processing (9)
  • app/build.gradle (1 hunks)
  • app/src/org/commcare/connect/network/ApiPersonalId.java (1 hunks)
  • app/src/org/commcare/connect/network/connectId/ApiEndPoints.java (1 hunks)
  • app/src/org/commcare/connect/network/connectId/ApiService.java (1 hunks)
  • app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (6 hunks)
  • app/src/org/commcare/utils/FirebaseAuthService.java (1 hunks)
  • app/src/org/commcare/utils/OtpAuthService.java (1 hunks)
  • app/src/org/commcare/utils/OtpManager.java (1 hunks)
  • app/src/org/commcare/utils/OtpVerificationCallback.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/src/org/commcare/connect/network/connectId/ApiService.java (1)
app/src/org/commcare/connect/network/connectId/ApiEndPoints.java (1)
  • ApiEndPoints (3-36)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Lint Code Base
🔇 Additional comments (5)
app/src/org/commcare/connect/network/connectId/ApiEndPoints.java (1)

8-8: LGTM!

The new API endpoint constant follows the established naming convention and is properly placed within the existing structure.

app/src/org/commcare/utils/OtpVerificationCallback.java (1)

1-33: Well-designed callback interface for OTP operations.

The interface is clean, well-documented, and follows good design principles with appropriate separation of concerns for the different OTP verification states.

app/src/org/commcare/utils/OtpManager.java (3)

22-24: Good defensive programming with null safety.

The constructor properly validates the required dependency using Objects.requireNonNull which will provide a clear error message if a null service is passed.


32-37: Effective input validation for phone number.

The validation logic properly checks for null, empty, and whitespace-only phone numbers before delegating to the service, preventing unnecessary processing.


45-50: Consistent input validation for OTP code.

The validation pattern matches the phone number validation, ensuring consistent error handling throughout the class.

implementation "androidx.camera:camera-camera2:$cameraX_version"
implementation "androidx.camera:camera-lifecycle:$cameraX_version"
implementation 'com.google.android.gms:play-services-mlkit-face-detection:17.1.0'
implementation "com.google.firebase:firebase-auth:22.3.0"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify Firebase Auth dependency version and security.

Please confirm that Firebase Auth version 22.3.0 is current and free from security vulnerabilities.


🌐 Web query:

What is the latest version of Firebase Auth for Android and are there any known security vulnerabilities in version 22.3.0?

💡 Result:

As of May 23, 2025, the latest version of Firebase Authentication for Android is 23.2.0. This version was released on March 20, 2025, as part of Firebase Android BoM (Bill of Materials) version 33.11.0. (firebase.google.com)

Version 22.3.0 of Firebase Authentication for Android was released on November 16, 2023. (mvnrepository.com) There are no known security vulnerabilities associated with this version. However, it's important to note that Firebase Authentication version 22.3.0 is outdated, and using the latest version is recommended to benefit from the latest features, improvements, and security patches.

Citations:


Upgrade Firebase Auth to latest version (23.2.0)

Firebase Auth 22.3.0 has no known security vulnerabilities but is outdated. Upgrading to 23.2.0 (released March 20, 2025) ensures you receive the latest features, improvements, and security patches.

• File: app/build.gradle
Line 168:

- implementation "com.google.firebase:firebase-auth:22.3.0"
+ implementation "com.google.firebase:firebase-auth:23.2.0"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
implementation "com.google.firebase:firebase-auth:22.3.0"
implementation "com.google.firebase:firebase-auth:23.2.0"
🤖 Prompt for AI Agents
In app/build.gradle at line 168, the Firebase Auth dependency is set to version
22.3.0, which is outdated. Update the version to 23.2.0 to ensure you have the
latest features, improvements, and security patches. Replace
"com.google.firebase:firebase-auth:22.3.0" with
"com.google.firebase:firebase-auth:23.2.0".

Comment on lines 296 to 302
public static void validateFirebaseIdToken(Context context, String firebaseIdToken, IApiCallback callback) {
HashMap<String, String> params = new HashMap<>();
params.put("token", firebaseIdToken);
ApiService apiService = ApiClient.getClientApi();
Call<ResponseBody> call = apiService.validateFirebaseIdToken(params);
callApi(context, call, callback);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for the Firebase ID token parameter.

The method should validate the firebaseIdToken parameter before making the API call to prevent unnecessary network requests with invalid data.

Apply this diff to add input validation:

 public static void validateFirebaseIdToken(Context context, String firebaseIdToken, IApiCallback callback) {
+    if (firebaseIdToken == null || firebaseIdToken.trim().isEmpty()) {
+        callback.processFailure(-1); // or appropriate error code
+        return;
+    }
     HashMap<String, String> params = new HashMap<>();
     params.put("token", firebaseIdToken);
     ApiService apiService = ApiClient.getClientApi();
     Call<ResponseBody> call = apiService.validateFirebaseIdToken(params);
     callApi(context, call, callback);
 }
🤖 Prompt for AI Agents
In app/src/org/commcare/connect/network/ApiPersonalId.java around lines 296 to
302, the method validateFirebaseIdToken does not validate the firebaseIdToken
parameter before using it. Add a check at the start of the method to verify that
firebaseIdToken is not null or empty; if it is invalid, invoke the callback with
an error or return early to avoid making the API call with invalid data.

Comment on lines 42 to 69
PhoneAuthOptions options = PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
.setPhoneNumber(phoneNumber) // Phone number to verify
.setActivity(activity) // Activity for binding callback
.setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

// Called when verification is completed automatically (instant/auto-retrieval)
@Override
public void onVerificationCompleted(@NonNull PhoneAuthCredential credential) {
signInWithCredential(credential);
}

// Called when verification process fails
@Override
public void onVerificationFailed(@NonNull FirebaseException e) {
callback.onFailure("Verification failed: " + e.getMessage());
}

// Called when OTP has been sent successfully
@Override
public void onCodeSent(@NonNull String id, @NonNull PhoneAuthProvider.ForceResendingToken token) {
verificationId = id; // Store verification ID to use during verification
callback.onCodeSent(id); // Notify client
}
})
.build();

// Start phone number verification process
PhoneAuthProvider.verifyPhoneNumber(options);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add timeout configuration for OTP verification.

Firebase PhoneAuth operations should include timeout configuration to prevent indefinite waiting and improve user experience.

Apply this diff to add timeout configuration:

 PhoneAuthOptions options = PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
         .setPhoneNumber(phoneNumber) // Phone number to verify
-        .setActivity(activity) // Activity for binding callback
+        .setActivity(activityRef.get()) // Activity for binding callback
+        .setTimeout(60L, java.util.concurrent.TimeUnit.SECONDS) // Timeout after 60 seconds
         .setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/src/org/commcare/utils/FirebaseAuthService.java between lines 42 and 69,
the PhoneAuthOptions builder lacks a timeout configuration for the OTP
verification process. To fix this, add a timeout duration and time unit using
the setTimeout(long timeout, TimeUnit unit) method on the PhoneAuthOptions
builder before calling build(). This will ensure the verification process does
not wait indefinitely and improves user experience.

Comment on lines 95 to 110
private void signInWithCredential(PhoneAuthCredential credential) {
FirebaseAuth.getInstance().signInWithCredential(credential)
.addOnCompleteListener(activity, task -> {
if (task.isSuccessful()) {
// Authentication successful
FirebaseUser user = task.getResult().getUser();
callback.onSuccess(user);
} else {
// Authentication failed
String error = (task.getException() != null)
? task.getException().getMessage()
: "Unknown error";
callback.onFailure("Sign-in failed: " + error);
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle Activity lifecycle in Firebase callback.

The Firebase authentication callback uses the Activity reference which may be null or destroyed when the callback executes.

Apply this diff to handle Activity lifecycle:

 private void signInWithCredential(PhoneAuthCredential credential) {
+    Activity currentActivity = activityRef.get();
+    if (currentActivity == null || currentActivity.isDestroyed()) {
+        callback.onFailure("Activity no longer available");
+        return;
+    }
+    
     FirebaseAuth.getInstance().signInWithCredential(credential)
-            .addOnCompleteListener(activity, task -> {
+            .addOnCompleteListener(currentActivity, task -> {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/src/org/commcare/utils/FirebaseAuthService.java around lines 95 to 110,
the Firebase authentication callback uses the Activity reference directly, which
may be null or destroyed when the callback executes. To fix this, check if the
activity is still valid and not finishing before proceeding with the callback
logic. If the activity is invalid, avoid calling the callback to prevent crashes
or leaks.

Comment on lines 20 to 21
private final Activity activity; // Activity context required for Firebase callbacks
private final OtpVerificationCallback callback; // Callback interface for OTP result events
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential memory leak risk with Activity reference.

Storing a direct reference to the Activity can cause memory leaks if the authentication process outlives the Activity lifecycle.

Consider using WeakReference<Activity> or pass the Activity context only when needed:

-private final Activity activity; // Activity context required for Firebase callbacks
+private final WeakReference<Activity> activityRef; // Weak reference to prevent memory leaks
 private final OtpVerificationCallback callback; // Callback interface for OTP result events

And update the constructor:

 public FirebaseAuthService(Activity activity, OtpVerificationCallback callback) {
-    this.activity = activity;
+    this.activityRef = new WeakReference<>(activity);
     this.callback = callback;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private final Activity activity; // Activity context required for Firebase callbacks
private final OtpVerificationCallback callback; // Callback interface for OTP result events
// Use a weak reference to the Activity to avoid leaking it if this service outlives the Activity
- private final Activity activity; // Activity context required for Firebase callbacks
+ private final WeakReference<Activity> activityRef; // Weak reference to prevent memory leaks
private final OtpVerificationCallback callback; // Callback interface for OTP result events
public FirebaseAuthService(Activity activity, OtpVerificationCallback callback) {
- this.activity = activity;
+ this.activityRef = new WeakReference<>(activity);
this.callback = callback;
}
🤖 Prompt for AI Agents
In app/src/org/commcare/utils/FirebaseAuthService.java around lines 20 to 21,
the direct reference to Activity can cause memory leaks if the authentication
outlives the Activity lifecycle. Change the Activity field to a
WeakReference<Activity> to avoid holding a strong reference. Update the
constructor to accept an Activity and wrap it in a WeakReference. Modify all
usages of the Activity field to retrieve the Activity via get() on the
WeakReference and handle the case where it may be null.

Comment on lines 280 to 284
private void requestOtp() {
otpRequestTime = new DateTime();
clearOtpError();
// TODO: Call Firebase to request OTP
otpManager.requestOtp(primaryPhone);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null check for primaryPhone before requesting OTP.

The primaryPhone could be null if the fragment arguments are missing or incomplete, which could cause issues in the OTP request.

Add validation:

 private void requestOtp() {
     otpRequestTime = new DateTime();
     clearOtpError();
+    if (primaryPhone == null || primaryPhone.isEmpty()) {
+        displayOtpError("Phone number is missing. Please go back and enter your phone number.");
+        return;
+    }
     otpManager.requestOtp(primaryPhone);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void requestOtp() {
otpRequestTime = new DateTime();
clearOtpError();
// TODO: Call Firebase to request OTP
otpManager.requestOtp(primaryPhone);
}
private void requestOtp() {
otpRequestTime = new DateTime();
clearOtpError();
if (primaryPhone == null || primaryPhone.isEmpty()) {
displayOtpError("Phone number is missing. Please go back and enter your phone number.");
return;
}
otpManager.requestOtp(primaryPhone);
}
🤖 Prompt for AI Agents
In
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java
around lines 280 to 284, add a null check for the variable primaryPhone before
calling otpManager.requestOtp(primaryPhone). If primaryPhone is null, avoid
making the OTP request to prevent potential errors. Implement a conditional
check that only calls requestOtp when primaryPhone is not null.

Comment on lines 286 to 300
private void verifyOtp() {
clearOtpError();
String otpCode = binding.customOtpView.getOtpValue();
// TODO: Call Firebase to verify OTP

if (verificationId == null) {
Toast.makeText(requireContext(), "Verification ID is missing. Send OTP first.", Toast.LENGTH_SHORT).show();
return;
}

if (!otpCode.isEmpty()){
otpManager.submitOtp(otpCode);
}else {
Toast.makeText(requireContext(), "Enter OTP", Toast.LENGTH_SHORT).show();
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use consistent error handling and improve code formatting.

The method uses Toast messages instead of the existing displayOtpError method, creating inconsistent user experience. Also, there's inconsistent spacing in the if statement.

Use consistent error handling:

 private void verifyOtp() {
     clearOtpError();
     String otpCode = binding.customOtpView.getOtpValue();

     if (verificationId == null) {
-        Toast.makeText(requireContext(), "Verification ID is missing. Send OTP first.", Toast.LENGTH_SHORT).show();
+        displayOtpError("Please request a new verification code.");
         return;
     }

-    if (!otpCode.isEmpty()){
+    if (!otpCode.isEmpty()) {
         otpManager.submitOtp(otpCode);
-    }else {
-        Toast.makeText(requireContext(), "Enter OTP", Toast.LENGTH_SHORT).show();
+    } else {
+        displayOtpError("Please enter the verification code.");
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void verifyOtp() {
clearOtpError();
String otpCode = binding.customOtpView.getOtpValue();
// TODO: Call Firebase to verify OTP
if (verificationId == null) {
Toast.makeText(requireContext(), "Verification ID is missing. Send OTP first.", Toast.LENGTH_SHORT).show();
return;
}
if (!otpCode.isEmpty()){
otpManager.submitOtp(otpCode);
}else {
Toast.makeText(requireContext(), "Enter OTP", Toast.LENGTH_SHORT).show();
}
}
private void verifyOtp() {
clearOtpError();
String otpCode = binding.customOtpView.getOtpValue();
if (verificationId == null) {
displayOtpError("Please request a new verification code.");
return;
}
if (!otpCode.isEmpty()) {
otpManager.submitOtp(otpCode);
} else {
displayOtpError("Please enter the verification code.");
}
}
🤖 Prompt for AI Agents
In
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java
around lines 286 to 300, replace the Toast messages used for error notifications
with calls to the existing displayOtpError method to maintain consistent error
handling. Also, fix the inconsistent spacing in the if-else statements by
ensuring proper spaces around parentheses and braces for better code
readability.

Comment on lines 128 to 160
private void validateFirebaseIdToken(String firebaseIdToken) {
ApiPersonalId.validateFirebaseIdToken(getActivity(),firebaseIdToken, new IApiCallback() {
@Override
public void processSuccess(int responseCode, InputStream responseData) {
navigateToNameEntry();
}

@Override
public void processFailure(int responseCode) {
Logger.log("ERROR", String.format(Locale.getDefault(), "Failed: %d", responseCode));
}

@Override
public void processNetworkFailure() {
ConnectNetworkHelper.showNetworkError(getActivity());
}

@Override
public void processTokenUnavailableError() {
ConnectNetworkHelper.handleTokenUnavailableException(requireActivity());
}

@Override
public void processTokenRequestDeniedError() {
ConnectNetworkHelper.handleTokenDeniedException(requireActivity());
}

@Override
public void processOldApiError() {
ConnectNetworkHelper.showOutdatedApiError(getActivity());
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling and user feedback for token validation failures.

The processFailure method only logs the error without informing the user. This could leave users confused when validation fails.

Add user feedback and fix formatting:

-        ApiPersonalId.validateFirebaseIdToken(getActivity(),firebaseIdToken, new IApiCallback() {
+        ApiPersonalId.validateFirebaseIdToken(getActivity(), firebaseIdToken, new IApiCallback() {
             @Override
             public void processSuccess(int responseCode, InputStream responseData) {
                 navigateToNameEntry();
             }

             @Override
             public void processFailure(int responseCode) {
                 Logger.log("ERROR", String.format(Locale.getDefault(), "Failed: %d", responseCode));
+                Toast.makeText(requireContext(), "Phone verification failed. Please try again.", Toast.LENGTH_LONG).show();
             }

Consider showing more specific error messages based on the response code (e.g., 401 for invalid token, 400 for malformed request).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void validateFirebaseIdToken(String firebaseIdToken) {
ApiPersonalId.validateFirebaseIdToken(getActivity(),firebaseIdToken, new IApiCallback() {
@Override
public void processSuccess(int responseCode, InputStream responseData) {
navigateToNameEntry();
}
@Override
public void processFailure(int responseCode) {
Logger.log("ERROR", String.format(Locale.getDefault(), "Failed: %d", responseCode));
}
@Override
public void processNetworkFailure() {
ConnectNetworkHelper.showNetworkError(getActivity());
}
@Override
public void processTokenUnavailableError() {
ConnectNetworkHelper.handleTokenUnavailableException(requireActivity());
}
@Override
public void processTokenRequestDeniedError() {
ConnectNetworkHelper.handleTokenDeniedException(requireActivity());
}
@Override
public void processOldApiError() {
ConnectNetworkHelper.showOutdatedApiError(getActivity());
}
});
}
private void validateFirebaseIdToken(String firebaseIdToken) {
- ApiPersonalId.validateFirebaseIdToken(getActivity(),firebaseIdToken, new IApiCallback() {
+ ApiPersonalId.validateFirebaseIdToken(getActivity(), firebaseIdToken, new IApiCallback() {
@Override
public void processSuccess(int responseCode, InputStream responseData) {
navigateToNameEntry();
}
@Override
public void processFailure(int responseCode) {
Logger.log("ERROR", String.format(Locale.getDefault(), "Failed: %d", responseCode));
+ Toast.makeText(requireContext(), "Phone verification failed. Please try again.", Toast.LENGTH_LONG).show();
}
@Override
public void processNetworkFailure() {
ConnectNetworkHelper.showNetworkError(getActivity());
}
@Override
public void processTokenUnavailableError() {
ConnectNetworkHelper.handleTokenUnavailableException(requireActivity());
}
@Override
public void processTokenRequestDeniedError() {
ConnectNetworkHelper.handleTokenDeniedException(requireActivity());
}
@Override
public void processOldApiError() {
ConnectNetworkHelper.showOutdatedApiError(getActivity());
}
});
}
🤖 Prompt for AI Agents
In
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java
between lines 128 and 160, the processFailure method only logs the error without
providing user feedback. Enhance this method by adding user-visible error
messages based on the responseCode, such as showing a specific message for 401
(invalid token) and 400 (malformed request), using appropriate UI elements like
Toast or Dialog. Also, improve the log formatting for clarity.

…est-connectid-verify-firebase-otp

# Conflicts:
#	app/src/org/commcare/connect/network/ApiPersonalId.java
#	app/src/org/commcare/connect/network/connectId/ApiEndPoints.java
@shubham1g5
Copy link
Contributor

@jaypanchal-13 think this should target https://github.com/dimagi/commcare-android/pull/3113/files and not master , given there are a lot of common changes here which are already getting reviewed as part of the other PR.

@shubham1g5 shubham1g5 added the skip-integration-tests Skip android tests. label May 27, 2025
@jaypanchal-13 jaypanchal-13 changed the base branch from master to CCCT-1157-sending-receiving-otp-firebase May 27, 2025 11:59
…159-send-request-connectid-verify-firebase-otp

# Conflicts:
#	app/src/org/commcare/connect/network/connectId/ApiEndPoints.java
#	app/src/org/commcare/connect/network/connectId/ApiService.java
@jaypanchal-13 jaypanchal-13 requested a review from pm-dimagi May 28, 2025 09:58
Base automatically changed from CCCT-1157-sending-receiving-otp-firebase to master May 28, 2025 10:14
@shubham1g5
Copy link
Contributor

@damagatchi retest this please

Comment on lines 72 to 74
if (savedInstanceState != null) {
primaryPhone = savedInstanceState.getString(KEY_PHONE);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaypanchal-13 Do we need this ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes for more clarification I created initOtpManager() which initialize otp callbacks and otpmanager

@Override
public void parse(JSONObject json, PersonalIdSessionData sessionData) throws JSONException {
if (json.has("error")) {
sessionData.setSessionFailureCode(json.getString("error"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this getting checked again to determine if it was a success/failure in the phone verification fragment. Am I missing something ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shubham1g5 No it is not getting checked again but I created it because I am using createCallback() so that I have to not write different failure states of api

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but should not it get checked to decide success vs failure in your fragment ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for checking success and failure state validateFirebaseIdToken() is created in fragment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you hardcode an error here to test and make sure when there is an error we call the failure pathway in fragment and not success.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified it in placing code in other screens failure is executing and not success

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anywhere thugh where we are directing to success vs failure based on getSessionFailureCode , Can you explain in detail with method call chain how is it working for you please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not pushed this testing code because it was just for testing purpose so you will not able to see it. getSessionFailureCode is not used to check success or failure state. FirebaseTokenValidationResponseParser is created just to use createCallback() so that I do not have to handle all the failure state(processFailure,processNetworkFailure etc) differently in fragment. And implemented this api /users/validate_firebase_id_token. I have created parser for it and tracked just failure state not success state. If I do not pass FirebaseTokenValidationResponseParser then it will through nullpointerexception so I created FirebaseTokenValidationResponseParser. Api is having response payload like

for failure
{
"error": "FAILED_VALIDATING_TOKEN"
}
for success
{
200
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think its not necessary to create parser here you can just put the necessary check at the level of create callback because there is no data coming back from the api just 200 or not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pm-dimagi Yes as I want to show error msg in toast so I created FirebaseTokenValidationResponseParser to catch error msg

@Override
public void parse(JSONObject json, PersonalIdSessionData sessionData) throws JSONException {
if (json.has("error")) {
sessionData.setSessionFailureCode(json.getString("error"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think its not necessary to create parser here you can just put the necessary check at the level of create callback because there is no data coming back from the api just 200 or not

// Handle validation errors
logNetworkError(response);
callback.processFailure(response.code());
callback.processFailureWithParser(response.body().byteStream());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when we are returning 1 failure what is the need of another one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pm-dimagi There is no parser to catch failure msg so created it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but according to this doc there is no error filed in response have u asked this with charl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pm-dimagi Yes it is not mentioned in doc but I have confirmed it with Zandre.

@pm-dimagi
Copy link
Contributor

@damagatchi retest this please

…est-connectid-verify-firebase-otp

# Conflicts:
#	app/res/values-fr/strings.xml
#	app/res/values-pt/strings.xml
#	app/res/values-sw/strings.xml
#	app/res/values-ti/strings.xml
#	app/res/values/strings.xml
#	app/src/org/commcare/connect/network/ApiPersonalId.java
#	app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.java
#	app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java
#	app/src/org/commcare/fragments/personalId/PersonalIdPhotoCaptureFragment.java
@shubham1g5
Copy link
Contributor

@jaypanchal-13 @pm-dimagi I made some final changes here to keep things simple for now around error handling, specially I don't think we need multiple error parsers, so not trying to have one at the moment.

@OrangeAndGreen OrangeAndGreen merged commit 77da34e into master Jun 2, 2025
5 of 8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jun 6, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-integration-tests Skip android tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants