Skip to content

Port session reverification support to Android#698

Open
swolfand wants to merge 4 commits into
mainfrom
sam/mobile-539-android-add-reverification-to-android-sdk
Open

Port session reverification support to Android#698
swolfand wants to merge 4 commits into
mainfrom
sam/mobile-539-android-add-reverification-to-android-sdk

Conversation

@swolfand

@swolfand swolfand commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add SessionVerification plus session reverification APIs for start, prepare, and attempt flows
  • Extend Session with reverification convenience methods, including password, TOTP, backup code, and passkey paths
  • Add the new /client/sessions/{id}/verify... endpoints and factor metadata needed by the flow

Testing

  • Added unit coverage for session verification serialization and API forwarding
  • :source:api:test passed
  • :source:api:spotlessCheck passed
  • :source:api:detekt passed

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Complex PR? Review this PR in Change Stack to move by importance, not file order.

Review Change Stack

Warning

Review limit reached

@seanperez29, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 14 minutes and 28 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 354f6a49-68c0-419a-abd7-95e5b170c8e4

📥 Commits

Reviewing files that changed from the base of the PR and between cfd3ef5 and eafe030.

📒 Files selected for processing (3)
  • source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt
  • source/api/src/test/java/com/clerk/api/passkeys/PasskeyAuthenticationServiceTest.kt
  • source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt
📝 Walkthrough

Walkthrough

This PR adds session in-session re-verification (step-up authentication) support to the Clerk Android SDK. It introduces the SessionVerification data model representing verification state, extends the SessionApi with five new Retrofit endpoints for starting verification and preparing/attempting first/second factor flows, and adds extension functions on Session to wire these APIs. The implementation supports password, TOTP, backup code, and passkey verification strategies. Passkey verification is integrated through GoogleCredentialAuthenticationService by refactoring credential retrieval to accept an explicit nonce and adding a new verifySessionWithPasskey function. The Factor model is extended with enterprise connection fields, and test coverage validates the new verification flows.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.76% 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
Title check ✅ Passed The title clearly and concisely describes the main change: porting session reverification support to Android, which directly aligns with the primary objective of adding SessionVerification and reverification APIs.
Description check ✅ Passed The description is complete and well-structured, covering summary of changes, testing details, and build check results. It aligns well with the PR objectives and provides sufficient context despite the minimal template.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt (2)

55-55: 💤 Low value

Consider refactoring instead of suppressing Detekt rules.

The @Suppress("TooManyFunctions") annotation bypasses Detekt static analysis. As per coding guidelines, Detekt should be used with all rules enabled. Consider extracting helper objects or splitting responsibilities to reduce the function count.

🤖 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
`@source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt`
at line 55, The class-level suppression `@Suppress`("TooManyFunctions") on
GoogleCredentialAuthenticationService hides a Detekt warning; remove the
suppression and split responsibilities by extracting cohesive helper classes or
objects (e.g., GoogleOAuthClient for external API calls, TokenValidator for
validation logic, CredentialMapper for mapping/transformations, and possibly a
GoogleCredentialRepository for persistence-related methods), move the
corresponding methods from GoogleCredentialAuthenticationService into these new
classes, update constructor/Dagger/Koin injection of the new collaborators into
GoogleCredentialAuthenticationService, and adjust unit tests; after refactoring,
re-run Detekt and ensure the original class no longer exceeds the function limit
so the suppression can be removed.

139-144: ⚡ Quick win

Consider adding null check for sign-in nonce.

Similar to the session verification flow, signIn.firstFactorVerification?.nonce (line 141) is nullable. If null, createWebAuthnRequest at line 373 will throw a generic requireNotNull error. Consider adding an explicit null check here as well for consistency and better error reporting.

📋 Suggested null check
       else -> {
         val activity = Clerk.credentialActivity()!!
         when (val createResult = createSignIn()) {
           is ClerkResult.Success -> {
             val signIn = createResult.value
+            val nonce = signIn.firstFactorVerification?.nonce
+            if (nonce == null) {
+              ClerkLog.e("Missing nonce in first factor verification for passkey sign-in")
+              return ClerkResult.unknownFailure(
+                IllegalStateException("First factor verification nonce is required for passkey authentication")
+              )
+            }
             try {
               val credential =
                 getCredentialFromManager(
                   activity = activity,
-                  nonce = signIn.firstFactorVerification?.nonce,
+                  nonce = nonce,
                   allowedCredentialIds = allowedCredentialIds,
                   credentialRequestTypes = credentialTypes,
                 )
🤖 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
`@source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt`
around lines 139 - 144, The call to getCredentialFromManager passes
signIn.firstFactorVerification?.nonce which is nullable and can cause a generic
requireNotNull failure later in createWebAuthnRequest; add an explicit null
check before calling getCredentialFromManager (e.g., validate
signIn.firstFactorVerification and its nonce) and return or throw a descriptive
error (with context like "missing first-factor verification nonce for sign-in")
so getCredentialFromManager / createWebAuthnRequest receive a non-null nonce;
update the code paths around the getCredentialFromManager invocation and any
callers that rely on the nonce to use this explicit validation.
source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt (1)

70-170: ⚡ Quick win

Consider adding failure case tests.

The current test suite thoroughly validates the success paths (ClerkResult.Success), but does not cover failure scenarios (ClerkResult.Failure). For authentication flows, it's valuable to verify that errors are properly propagated and handled.

Consider adding tests that mock ClerkResult.Failure responses from the SessionApi methods to ensure the extension functions correctly forward error states. As per coding guidelines, regression tests should be added when altering auth flows.

🤖 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 `@source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt`
around lines 70 - 170, Add tests that cover ClerkResult.Failure paths by mocking
ClerkApi.session to return ClerkResult.Failure from the same SessionApi methods
already used in success tests (startVerification,
prepareFirstFactorVerification, attemptFirstFactorVerification,
attemptSecondFactorVerification) and asserting the session extension methods
(Session.startVerification, Session.prepareFirstFactorVerification,
Session.verifyWithPassword, Session.verifyWithTOTP,
Session.verifyWithBackupCode) return ClerkResult.Failure and that the
corresponding SessionApi method was invoked (use coEvery to return
ClerkResult.failure(...) and coVerify to check call count); mirror the existing
success-test structure (mockkObject(ClerkApi), every { ClerkApi.session }
returns sessionApi, coEvery { sessionApi.<method>("sess_123", params) } returns
ClerkResult.failure(...), call the session method, assert result is
ClerkResult.Failure, and coVerify(exactly = 1) the API call).
🤖 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
`@source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt`:
- Around line 186-232: In verifySessionWithPasskey, check
preparedVerification.firstFactorVerification?.nonce for null before calling
getCredentialFromManager; if it's null, log a clear error like "Missing nonce in
preparedVerification.firstFactorVerification" and return a ClerkResult failure
(e.g., ClerkResult.unknownFailure(IllegalStateException("Missing nonce in
prepared verification"))) instead of proceeding to getCredentialFromManager so
the later requireNotNull crash in createWebAuthnRequest is avoided and the error
is reported clearly.

In
`@source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationExtensions.kt`:
- Around line 51-64: The attemptFirstFactorVerification function currently
prefers password > publicKeyCredential > code and throws on invalid input;
instead validate inputs so exactly one of code, password, or publicKeyCredential
is provided and ensure a code must be paired with a non-empty strategy while
password/passkey must not be combined with a strategy mismatch; when validation
fails return a ClerkResult failure (ClerkResult.Failure/ClerkResult.Error
variant using ClerkErrorResponse) rather than calling error(), and construct
Session.AttemptFirstFactorParams only after validation (refer to
Session.attemptFirstFactorVerification and
Session.AttemptFirstFactorParams.Password/Passkey/Code to locate the code).
Ensure the function uses pattern matching on ClerkResult sealed types for the
result path and does not throw exceptions for input validation errors.

In `@source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt`:
- Around line 19-190: Add a unit test in SessionVerificationTest.kt that covers
Session.verifyWithPasskey by mocking PasskeyService.verifySessionWithPasskey:
create a session via testSession(), mock ClerkApi.session if needed and
mockkObject(PasskeyService) or the relevant service object, set coEvery {
PasskeyService.verifySessionWithPasskey("sess_123", any(), any()) } to return
ClerkResult.success(testVerification(...)) and an error case, call
session.verifyWithPasskey(allowedCredentialIds = listOf("cred_1","cred_2")),
assert the result is ClerkResult.Success and the returned SessionVerification is
the same instance, and coVerify that verifySessionWithPasskey was invoked with
"sess_123" and the expected allowedCredentialIds to ensure parameters are
forwarded correctly.

---

Nitpick comments:
In
`@source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt`:
- Line 55: The class-level suppression `@Suppress`("TooManyFunctions") on
GoogleCredentialAuthenticationService hides a Detekt warning; remove the
suppression and split responsibilities by extracting cohesive helper classes or
objects (e.g., GoogleOAuthClient for external API calls, TokenValidator for
validation logic, CredentialMapper for mapping/transformations, and possibly a
GoogleCredentialRepository for persistence-related methods), move the
corresponding methods from GoogleCredentialAuthenticationService into these new
classes, update constructor/Dagger/Koin injection of the new collaborators into
GoogleCredentialAuthenticationService, and adjust unit tests; after refactoring,
re-run Detekt and ensure the original class no longer exceeds the function limit
so the suppression can be removed.
- Around line 139-144: The call to getCredentialFromManager passes
signIn.firstFactorVerification?.nonce which is nullable and can cause a generic
requireNotNull failure later in createWebAuthnRequest; add an explicit null
check before calling getCredentialFromManager (e.g., validate
signIn.firstFactorVerification and its nonce) and return or throw a descriptive
error (with context like "missing first-factor verification nonce for sign-in")
so getCredentialFromManager / createWebAuthnRequest receive a non-null nonce;
update the code paths around the getCredentialFromManager invocation and any
callers that rely on the nonce to use this explicit validation.

In `@source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt`:
- Around line 70-170: Add tests that cover ClerkResult.Failure paths by mocking
ClerkApi.session to return ClerkResult.Failure from the same SessionApi methods
already used in success tests (startVerification,
prepareFirstFactorVerification, attemptFirstFactorVerification,
attemptSecondFactorVerification) and asserting the session extension methods
(Session.startVerification, Session.prepareFirstFactorVerification,
Session.verifyWithPassword, Session.verifyWithTOTP,
Session.verifyWithBackupCode) return ClerkResult.Failure and that the
corresponding SessionApi method was invoked (use coEvery to return
ClerkResult.failure(...) and coVerify to check call count); mirror the existing
success-test structure (mockkObject(ClerkApi), every { ClerkApi.session }
returns sessionApi, coEvery { sessionApi.<method>("sess_123", params) } returns
ClerkResult.failure(...), call the session method, assert result is
ClerkResult.Failure, and coVerify(exactly = 1) the API call).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5da85b88-cb45-4b2c-9d9c-e615318504ef

📥 Commits

Reviewing files that changed from the base of the PR and between 63078f8 and e5e8982.

📒 Files selected for processing (10)
  • source/api/src/main/kotlin/com/clerk/api/network/ApiPaths.kt
  • source/api/src/main/kotlin/com/clerk/api/network/api/SessionApi.kt
  • source/api/src/main/kotlin/com/clerk/api/network/model/factor/Factor.kt
  • source/api/src/main/kotlin/com/clerk/api/passkeys/GoogleCredentialAuthenticationService.kt
  • source/api/src/main/kotlin/com/clerk/api/passkeys/PasskeyService.kt
  • source/api/src/main/kotlin/com/clerk/api/session/Session.kt
  • source/api/src/main/kotlin/com/clerk/api/session/SessionVerification.kt
  • source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationConvenienceExtensions.kt
  • source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationExtensions.kt
  • source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt

Comment thread source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationExtensions.kt Outdated

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt (1)

191-223: 💤 Low value

Consider isolating global state mutations in tests.

The test mutates Clerk.applicationId (line 197) and restores it in a finally block (line 221). While the cleanup is handled correctly, mutating global state can lead to test fragility or race conditions if tests ever run in parallel.

Consider mocking Clerk as an object or using dependency injection to make the test more isolated and robust.

🤖 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 `@source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt`
around lines 191 - 223, The test mutates global Clerk.applicationId; instead
avoid changing global state by mocking Clerk as an object and stubbing its
applicationId for the test (or inject applicationId into the Session under
test). Replace the direct assignment to Clerk.applicationId with
mockkObject(Clerk) and stub every { Clerk.applicationId } returns
"com.clerk.test" before calling session.startEnterpriseSso, and then
unmockkObject(Clerk) in finally (or prefer constructor/DI to pass the app id
into Session and update SessionVerificationTest to supply it), ensuring you
still verify sessionApi.prepareFirstFactorVerification with the expected params.
🤖 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.

Nitpick comments:
In `@source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt`:
- Around line 191-223: The test mutates global Clerk.applicationId; instead
avoid changing global state by mocking Clerk as an object and stubbing its
applicationId for the test (or inject applicationId into the Session under
test). Replace the direct assignment to Clerk.applicationId with
mockkObject(Clerk) and stub every { Clerk.applicationId } returns
"com.clerk.test" before calling session.startEnterpriseSso, and then
unmockkObject(Clerk) in finally (or prefer constructor/DI to pass the app id
into Session and update SessionVerificationTest to supply it), ensuring you
still verify sessionApi.prepareFirstFactorVerification with the expected params.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a650c5a-9453-4f79-b33a-7646efa37287

📥 Commits

Reviewing files that changed from the base of the PR and between e5e8982 and cfd3ef5.

📒 Files selected for processing (4)
  • source/api/src/main/kotlin/com/clerk/api/session/Session.kt
  • source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationConvenienceExtensions.kt
  • source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationExtensions.kt
  • source/api/src/test/java/com/clerk/api/session/SessionVerificationTest.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • source/api/src/main/kotlin/com/clerk/api/session/Session.kt
  • source/api/src/main/kotlin/com/clerk/api/session/SessionVerificationExtensions.kt

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