Skip to content

fix: MFAClient getAuthenticators filtering based on Authenticator.type field#998

Merged
utkrishtsahu merged 4 commits into
mainfrom
fix/mfa-getauthenticators-swift-parity
Jul 8, 2026
Merged

fix: MFAClient getAuthenticators filtering based on Authenticator.type field#998
utkrishtsahu merged 4 commits into
mainfrom
fix/mfa-getauthenticators-swift-parity

Conversation

@utkrishtsahu

@utkrishtsahu utkrishtsahu commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Changes

This PR contains two related MFA fixes.

1. getAuthenticators — filter on Authenticator.type (exact match)

Updates how MfaApiClient.getAuthenticators(factorsAllowed) filters the authenticators returned by GET /mfa/authenticators.

What changed

  • Filtering logic — Authenticators are now filtered by comparing each Authenticator.type value directly against the factorsAllowed list using exact (case-sensitive) equality. Previously the SDK derived an "effective type" by inspecting oob_channel and authenticator_type (with aliasing, e.g. sms/phone, otp/totp, and treating oob as a wildcard). That derivation and its helpers have been removed.
  • Methods removed — the private helpers matchesFactorType(...) and getEffectiveType(...) in MfaApiClient.
  • No endpoint or public method signature changesgetAuthenticators(factorsAllowed: List<String>) keeps the same signature; only its internal filtering behavior changed.
  • DocumentationMfaApiClient KDoc and EXAMPLES.md updated so the factorsAllowed examples use the same factor values as the type field (e.g. phone, email, otp, recovery-code) instead of channel-level values like sms/oob.

Why it matters

The factorsAllowed values now map one-to-one to the Authenticator.type field, so filtering is predictable and matches the values a caller gets from MfaRequirements (MfaFactor.type). This is a behavioral change: callers passing channel-level or wildcard values such as sms or oob will no longer match — they should pass the resolved type value (e.g. phone). Confirmed against live FGS that a TOTP factor has type: "totp" (authenticator_type: "otp"), so callers must pass totp to surface it.

2. TotpEnrollmentChallenge — correct field mapping for /mfa/associate

TotpEnrollmentChallenge is shared by two endpoints with different response shapes, but was modeled only on the My Account /authentication-methods shape. Used against the ROPG /mfa/associate endpoint (which MfaApiClient.enroll(Otp) calls), this caused:

  • id and authSession declared non-null but populated as null by Gson (null-safety bypass / latent NPE hazard),
  • manualInputCode always null (the endpoint returns the key as secret, not manual_input_code),
  • secret and recovery_codes silently dropped.

What changed

  • Made id and authSession nullable (manualInputCode was already nullable) and added authenticatorType, secret, recoveryCodes, so a single class deserializes both endpoints correctly. This matches Auth0.swift's OTPMFAEnrollmentChallenge (the only cross-SDK peer that implements /mfa/associate).
  • My Account flow unaffected — its fields (id/authSession/manualInputCode/barcodeUri) still map identically; the change only loosens nullability and adds fields. No runtime break for existing users; recompiling My Account consumers may need a null check on id/authSession (the safe direction).
  • DocumentationEXAMPLES.md MFA enroll(Otp) snippet corrected to read secret (not manualInputCode) and now shows recoveryCodes.

Usage

// factorsAllowed values map directly to Authenticator.type
val factorTypes = requirements?.challenge?.map { it.type } ?: emptyList()

mfaClient
    .getAuthenticators(factorsAllowed = factorTypes) // e.g. ["otp", "phone", "email", "recovery-code"]
    .start(object : Callback<List<Authenticator>, MfaListAuthenticatorsException> {
        override fun onSuccess(result: List<Authenticator>) {
            // Only authenticators whose `type` matches one of factorsAllowed are returned
        }
        override fun onFailure(error: MfaListAuthenticatorsException) { }
    })

// Enrolling TOTP via /mfa/associate
mfaClient
    .enroll(MfaEnrollmentType.Otp)
    .start(object : Callback<EnrollmentChallenge, MfaEnrollmentException> {
        override fun onSuccess(enrollment: EnrollmentChallenge) {
            if (enrollment is TotpEnrollmentChallenge) {
                val secret = enrollment.secret          // manual-entry key
                val barcodeUri = enrollment.barcodeUri  // otpauth:// URI for QR
                val recoveryCodes = enrollment.recoveryCodes
            }
        }
        override fun onFailure(error: MfaEnrollmentException) { }
    })

References

Testing

  • getAuthenticators unit tests in MfaApiClientTest exercise exact-type matching: fixtures include a top-level type field and assert only authenticators whose type is in factorsAllowed are returned.

  • TOTP enroll test updated to use the real /mfa/associate response shape, asserting secret/recoveryCodes/authenticatorType populate.

  • MyAccountAPIClientTest re-run to confirm the shared-class change does not affect the My Account TOTP/push flows.

  • Verified end-to-end on a physical device (Pixel 7a) against live FGS: login → enroll OTP → verify OTP → verify recovery code, all succeeded; secret/recovery_codes populate correctly.

  • This change adds unit test coverage

  • This change adds integration test coverage

  • This change has been tested on the latest version of the platform/language or why not

Checklist

Summary by CodeRabbit

  • Bug Fixes
    • Improved MFA authenticator filtering to use the exact factor type you request, resulting in more consistent authenticator lists and smoother enrollment.
    • Updated TOTP enrollment handling to correctly capture and expose the latest response fields (including secret and recovery codes), improving setup details.
  • Documentation
    • Refreshed MFA Kotlin/Java examples to use the correct factor names and updated TOTP enrollment fields to match current responses.

@utkrishtsahu utkrishtsahu requested a review from a team as a code owner July 1, 2026 10:26
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b353a0b-a89b-4826-922b-bc593fe4384b

📥 Commits

Reviewing files that changed from the base of the PR and between 0d82101 and 36dcaf4.

📒 Files selected for processing (1)
  • auth0/src/main/java/com/auth0/android/result/EnrollmentChallenge.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • auth0/src/main/java/com/auth0/android/result/EnrollmentChallenge.kt

📝 Walkthrough

Walkthrough

MFA authenticator filtering now matches exact authenticator types, and TOTP enrollment handling now uses the updated associate response fields across the model, tests, and examples.

Changes

MFA authenticator filtering and examples

Layer / File(s) Summary
Exact type filtering
auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt, auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt, EXAMPLES.md
getAuthenticators now filters by exact Authenticator.type equality, with updated KDoc and test payloads/assertions using type-bearing authenticators and phone examples instead of sms.

TOTP enrollment fields and examples

Layer / File(s) Summary
Updated enrollment challenge fields
auth0/src/main/java/com/auth0/android/result/EnrollmentChallenge.kt, auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt, EXAMPLES.md
TotpEnrollmentChallenge now models optional associate fields such as secret and recoveryCodes, and the enrollment tests and Kotlin/Java examples were updated to read the new fields.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main behavioral change: filtering authenticators by Authenticator.type.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mfa-getauthenticators-swift-parity

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt (1)

305-319: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Same "phone" vs "oob" concern applies to this doc block.

This restates the exact-match filtering contract using factorsAllowed = ["phone", "email", "otp"] as an example. Same concern as the public KDoc above — please confirm this matches the real Authenticator.type values returned by GET /mfa/authenticators.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt`
around lines 305 - 319, The KDoc for the JSON adapter in MfaApiClient still uses
examples that may not match the real Authenticator.type values returned by GET
/mfa/authenticators. Update the doc block to use only documented/verified
factor-type examples and make the exact-match filtering contract consistent with
the actual API response values; locate the affected text in the adapter
documentation near the filtering description.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt`:
- Around line 127-140: The KDoc example in MfaApiClient.getAuthenticators uses
outdated factor values that won’t match SMS/email authenticators. Update the
sample callback and the `@param` factorsAllowed examples to use "oob" instead of
"phone" and "email", keeping the exact-match filtering note aligned with
Authenticator.type.

---

Duplicate comments:
In `@auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt`:
- Around line 305-319: The KDoc for the JSON adapter in MfaApiClient still uses
examples that may not match the real Authenticator.type values returned by GET
/mfa/authenticators. Update the doc block to use only documented/verified
factor-type examples and make the exact-match filtering contract consistent with
the actual API response values; locate the affected text in the adapter
documentation near the filtering description.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 653affda-cf64-4839-b7ef-2ed4fdc1f553

📥 Commits

Reviewing files that changed from the base of the PR and between aa6581c and d727283.

📒 Files selected for processing (3)
  • EXAMPLES.md
  • auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt
  • auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt

NandanPrabhu
NandanPrabhu previously approved these changes Jul 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@auth0/src/main/java/com/auth0/android/result/EnrollmentChallenge.kt`:
- Around line 72-86: Keep TotpEnrollmentChallenge.id and authSession backward
compatible by avoiding a public API change from non-null to nullable in
EnrollmentChallenge.kt. Update the TotpEnrollmentChallenge definition so
existing callers can still rely on these properties as non-null, or otherwise
provide a compatibility-preserving alternative in the
TotpEnrollmentChallenge/EnrollmentChallenge hierarchy. If the nullability change
is unavoidable, document the breaking change clearly in release notes and API
docs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f260e646-fa53-4d86-94f6-b2dfcc6e5c87

📥 Commits

Reviewing files that changed from the base of the PR and between d727283 and 33509c7.

📒 Files selected for processing (3)
  • EXAMPLES.md
  • auth0/src/main/java/com/auth0/android/result/EnrollmentChallenge.kt
  • auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt

Comment thread auth0/src/main/java/com/auth0/android/result/EnrollmentChallenge.kt
pmathew92
pmathew92 previously approved these changes Jul 8, 2026
override val id: String? = null,
@SerializedName("auth_session")
override val authSession: String,
override val authSession: String? = null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Document these nullable property change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added.

@NandanPrabhu NandanPrabhu changed the title fix(mfa): match getAuthenticators by Authenticator.type field fix: MFAClient getAuthenticators filtering based on Authenticator.type field Jul 8, 2026
NandanPrabhu
NandanPrabhu previously approved these changes Jul 8, 2026
@utkrishtsahu utkrishtsahu dismissed stale reviews from NandanPrabhu and pmathew92 via 36dcaf4 July 8, 2026 07:07
@utkrishtsahu utkrishtsahu merged commit ea0cc66 into main Jul 8, 2026
7 checks passed
@utkrishtsahu utkrishtsahu deleted the fix/mfa-getauthenticators-swift-parity branch July 8, 2026 07:20
@utkrishtsahu utkrishtsahu mentioned this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants