Skip to content

feat: Added DPoP support for MFA APIs#992

Merged
pmathew92 merged 4 commits into
mainfrom
mfa_dpop
Jun 29, 2026
Merged

feat: Added DPoP support for MFA APIs#992
pmathew92 merged 4 commits into
mainfrom
mfa_dpop

Conversation

@pmathew92

@pmathew92 pmathew92 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Changes

This PR adds DPoP support to MFAApiClient class.

Checklist

Summary by CodeRabbit

  • New Features
    • Added DPoP sender-constraining support for MFA verify, including inheritance from the main authentication client.
    • You can explicitly enable DPoP on the MFA client for chaining behavior.
  • Bug Fixes
    • DPoP verification failures now report a clearer DPoP-specific MFA verify error.
    • DPoP proofs are not sent for MFA operations that don’t perform the token exchange (challenge/enroll/get-authenticators).
  • Documentation
    • Updated MFA documentation to explain how DPoP applies to verify.
  • Tests
    • Expanded automated tests to cover DPoP headers, inheritance, and error wrapping behavior.

@coderabbitai

coderabbitai Bot commented Jun 29, 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: 39fcecca-6ae8-4ea6-af5d-9a511809ddce

📥 Commits

Reviewing files that changed from the base of the PR and between 99877b8 and c40e54b.

📒 Files selected for processing (2)
  • auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt
  • auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt
  • auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt

📝 Walkthrough

Walkthrough

MfaApiClient now supports DPoP for MFA verification, AuthenticationAPIClient forwards DPoP state into MFA client creation, tests cover the new flows, and the MFA examples document the behavior.

Changes

DPoP MFA integration

Layer / File(s) Summary
AuthenticationAPIClient wiring
auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt
mfaClient(mfaToken) now passes the shared gson and dPoP state to MfaApiClient, and the KDoc notes DPoP inheritance.
MfaApiClient DPoP verify flow
auth0/src/main/java/com/auth0/android/authentication/mfa/MfaApiClient.kt
MfaApiClient implements SenderConstraining, stores optional dPoP, exposes useDPoP(context), includes dPoP in the verify token request, and maps DPoPException to MfaVerifyException with code dpop_error.
MFA tests and examples
auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt, EXAMPLES.md
The test setup mocks DPoP dependencies and covers verify header attachment, failure wrapping, and non-verify calls; the examples document DPoP inheritance and direct enablement.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • utkrishtsahu
  • subhankarmaiti

Poem

🐇 Hop, hop, the proofs now glide,
MFA and DPoP walk side by side.
Verify gets its little spark,
And bunny ears twitch in the dusk and dark.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% 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 and concisely summarizes the main change: adding DPoP support to MFA APIs.
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 mfa_dpop

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.

@pmathew92 pmathew92 marked this pull request as ready for review June 29, 2026 07:05
@pmathew92 pmathew92 requested a review from a team as a code owner June 29, 2026 07:05

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt (1)

115-119: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document inherited DPoP behavior.

Line 119 makes mfaClient() inherit this client’s DPoP configuration, but the public KDoc does not mention that behavior.

📝 Proposed KDoc update
-     * `@return` A new [MfaApiClient] instance configured for the transaction.
+     * `@return` A new [MfaApiClient] instance configured for the transaction. If this client
+     * has DPoP enabled via [useDPoP], the MFA client inherits that configuration.

As per coding guidelines, “Update relevant documentation (README.md, EXAMPLES.md, KDoc comments) when changing public APIs”.

🤖 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/AuthenticationAPIClient.kt`
around lines 115 - 119, The public KDoc for AuthenticationAPIClient.mfaClient
currently omits that the returned MfaApiClient inherits this client’s DPoP
configuration. Update the KDoc on mfaClient(mfaToken: String) to explicitly
document this inherited DPoP behavior so the public API contract matches the
implementation.

Source: Coding guidelines

auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt (1)

60-75: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Restore the global DPoP key store after each test.

Line 75 replaces DPoPUtil.keyStore globally, but tearDown() does not restore it, which can leak the mock into other test classes.

🧪 Proposed test isolation fix
     private lateinit var gson: Gson
     private lateinit var mockKeyStore: DPoPKeyStore
     private lateinit var mockContext: Context
+    private lateinit var originalKeyStore: DPoPKeyStore

     `@Before`
     public fun setUp(): Unit {
         mockServer = SSLTestUtils.createMockWebServer()
         mockServer.start()
@@
         auth0.networkingClient = SSLTestUtils.testClient
         mfaClient = MfaApiClient(auth0, MFA_TOKEN)
         gson = GsonBuilder().serializeNulls().create()
+        originalKeyStore = DPoPUtil.keyStore
         mockKeyStore = mock()
         mockContext = mock()
         whenever(mockContext.applicationContext).thenReturn(mockContext)
         DPoPUtil.keyStore = mockKeyStore
     }

     `@After`
     public fun tearDown(): Unit {
+        DPoPUtil.keyStore = originalKeyStore
         mockServer.shutdown()
     }

Also applies to: 78-81

🤖 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/test/java/com/auth0/android/authentication/MfaApiClientTest.kt`
around lines 60 - 75, The test setup in MfaApiClientTest is replacing the global
DPoPUtil.keyStore with a mock, but the teardown path does not restore the
original store, which can leak state into other tests. Update the class-level
test lifecycle around setUp/tearDown to save the previous DPoPUtil.keyStore
before assigning mockKeyStore, then restore it after each test; use the existing
MfaApiClientTest, setUp(), tearDown(), and DPoPUtil.keyStore symbols to locate
the change.
🤖 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/test/java/com/auth0/android/authentication/MfaApiClientTest.kt`:
- Around line 113-214: Add callback-path coverage for DPoP verify in
MfaApiClientTest by exercising the verify flow through start(callback) instead
of only await(), using the existing MfaApiClient.useDPoP and
MfaVerificationType.Otp setup. Reuse the DPoP-enabled test pattern to assert the
request still hits /oauth/token with the DPoP header, and add a callback-based
case that verifies callback/error adaptation behavior matches the coroutine
path.

---

Outside diff comments:
In
`@auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt`:
- Around line 115-119: The public KDoc for AuthenticationAPIClient.mfaClient
currently omits that the returned MfaApiClient inherits this client’s DPoP
configuration. Update the KDoc on mfaClient(mfaToken: String) to explicitly
document this inherited DPoP behavior so the public API contract matches the
implementation.

In `@auth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.kt`:
- Around line 60-75: The test setup in MfaApiClientTest is replacing the global
DPoPUtil.keyStore with a mock, but the teardown path does not restore the
original store, which can leak state into other tests. Update the class-level
test lifecycle around setUp/tearDown to save the previous DPoPUtil.keyStore
before assigning mockKeyStore, then restore it after each test; use the existing
MfaApiClientTest, setUp(), tearDown(), and DPoPUtil.keyStore symbols to locate
the change.
🪄 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: badf1c3d-67ea-41da-9151-0d7b9d5c0986

📥 Commits

Reviewing files that changed from the base of the PR and between d9ff93b and a0032e4.

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

mockKeyStore = mock()
mockContext = mock()
whenever(mockContext.applicationContext).thenReturn(mockContext)
DPoPUtil.keyStore = mockKeyStore

@utkrishtsahu utkrishtsahu Jun 29, 2026

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.

Small thing — since setUp() swaps DPoPUtil.keyStore for a mock and it's a static, mind resetting it back to DPoPKeyStore() here? Otherwise the mock can bleed into other test classes. Not a big deal (the other client tests don't do it either and setUp reassigns each run).

}

@Test
public fun shouldWrapDPoPExceptionAsMfaVerifyException(): Unit {

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.

using getKeyPair() returning null to force the DPoPException works, but it leans on an internal detail of DPoPUtil. Might read cleaner to stub it to throw the DPoPException directly so the test says what it means and won't break if that null-handling ever changes. Totally optional.

@pmathew92 pmathew92 merged commit ac30598 into main Jun 29, 2026
7 checks passed
@pmathew92 pmathew92 deleted the mfa_dpop branch June 29, 2026 09:00
@pmathew92 pmathew92 mentioned this pull request Jun 29, 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.

2 participants