Skip to content

fix(api): fix deletePassword and revokeSession, which threw on first call - #997

Merged
mikepitre merged 2 commits into
mainfrom
mike/fix-retrofit-service-methods
Oct 1, 2026
Merged

mikepitre merged 2 commits into
mainfrom
mike/fix-retrofit-service-methods

Conversation

@mikepitre

@mikepitre mikepitre commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

What & why

  • User.deletePassword() threw IllegalArgumentException("Missing either @POST URL or @Url parameter.") instead of returning a ClerkResult. UserApi.deletePassword had a bare @POST, and nothing used ApiPaths.User.Password.DELETE. It now posts to me/remove_password. This matches the FAPI spec: a form-encoded current_password field, and a 200 returns the user.
  • Session.revoke() and Auth.revokeSession() threw A @Path parameter must not come after a @Query. because SessionApi.revokeSession declared its @Query before its @Path. This PR swaps the two parameters. The only caller passes named arguments, so no call sites change.
  • With the order fixed, Session.revoke() reached the server but got 401 authentication_invalid, because revokeSession defaulted _clerk_session_id to null and Session.revoke() never passes it. It now defaults to the active session, like the other /me endpoints in UserApi. SessionRevokeTest drives Session.revoke() through a real Retrofit SessionApi and checks that the request URL carries the active session id. A teammate checked this on an emulator: revoking from UserProfile > Security > Active devices crashes on main, returns 401 with only the reorder, and returns 200 with this fix.
  • The per-API tests mock these interfaces, so no test ever made Retrofit parse their annotations. The new ServiceInterfaceValidationTest builds every service that ClerkApi creates with validateEagerly(true). An invalid method now fails in CI instead of on a user's first call. Before the fix, the test failed on both methods above.

What to focus on

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved session revocation requests so they identify the session being revoked and use the active session for authentication by default.
    • Corrected password deletion requests to use the designated password endpoint.
  • Tests
    • Added checks that API services pass request validation and that session revocation sends the correct session identifiers.

…call

UserApi.deletePassword had a bare @post, so Retrofit threw "Missing
either @post URL or @url parameter." the first time User.deletePassword()
ran. Point it at ApiPaths.User.Password.DELETE (POST me/remove_password).

SessionApi.revokeSession declared its @query before its @path, which
Retrofit rejects, so Session.revoke() threw too. Put the @path first.

The per-API tests mock these interfaces, so Retrofit never parsed them.
ServiceInterfaceValidationTest builds every service ClerkApi creates
with validateEagerly(true). DeviceAttestationApi is excluded: its
getChallenge() returns void and nothing has called it since #571.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: clerk/clerk-android/.coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 81dfc645-adcb-4bfd-8f48-e1f005fa68b2

📥 Commits

Reviewing files that changed from the base of the PR and between e685a0d and adc0827.

📒 Files selected for processing (2)
  • source/api/src/main/kotlin/com/clerk/api/network/api/SessionApi.kt
  • source/api/src/test/java/com/clerk/api/session/SessionRevokeTest.kt

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.


📝 Walkthrough

Walkthrough

The session revocation declaration now lists the target session ID before the optional requesting-session ID, which defaults to the active Clerk session. Password deletion now specifies its API route. Tests cover the revocation request and Retrofit service validation.

Changes

API Declarations

Layer / File(s) Summary
Session revocation request
source/api/src/main/kotlin/com/clerk/api/network/api/SessionApi.kt, source/api/src/test/java/com/clerk/api/session/SessionRevokeTest.kt
revokeSession places the target session ID before the requesting-session ID and defaults the latter to the active session ID. The test checks the target session path and active-session query parameter.
Password deletion route and service validation
source/api/src/main/kotlin/com/clerk/api/network/api/UserApi.kt, source/api/src/test/java/com/clerk/api/network/api/ServiceInterfaceValidationTest.kt
deletePassword uses ApiPaths.User.Password.DELETE. The test discovers service interfaces and checks them with eager Retrofit validation.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: swolfand

Merge Risk: ⚪ Minimal · up to adc08

The changes correct password deletion and session revocation requests without an identified contract regression. Mergeable subject to normal build and test checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the two primary fixes: deletePassword and revokeSession failures on their first call.
Description check ✅ Passed The description covers why the changes are needed, the implementation details, test coverage, validation risks, and deferred work. The optional Screenshots / video section is omitted, but the descript…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

With the parameter order fixed, Session.revoke() reached the server but
got 401 authentication_invalid: revokeSession defaulted _clerk_session_id
to null and Session.revoke() never passes it. Default it to the active
session, like the other /me endpoints in UserApi.

SessionRevokeTest drives Session.revoke() through a real Retrofit
SessionApi and asserts the request URL carries the active session id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mikepitre

Copy link
Copy Markdown
Collaborator Author

Verified on an Android emulator

Setup. Workbench app on a clean Pixel (API 36) emulator, test instance amusing-barnacle-26, the test user signed in with several sessions. Path: UserButton > Security > Active devices > ⋮ > "Sign out of this device". This section only renders with the active-sessions decoding fix from #1002, so both builds below include it.

Without this PR: tapping "Sign out of this device" crashes the app:

FATAL EXCEPTION: DefaultDispatcher-worker-2
java.lang.IllegalArgumentException: A @Path parameter must not come after a @Query. (parameter #2)
    for method SessionApi.revokeSession
pr997-crash-without.mp4

With this PR at adc0827: there's no crash. POST /v1/me/sessions/{id}/revoke?_clerk_session_id=… returns 200, and the session is gone the next time Security opens.

pr997-signout-works.mp4

At the earlier head, e685a0d, the crash was gone but the request carried no _clerk_session_id. The server answered 401 authentication_invalid ("you need to supply an active session"). adc0827 fixes that, and the recording above is on that commit.

Not covered here. Right after a successful sign-out, the device list still shows the revoked session until Security reopens. DeviceViewModel re-fetches active sessions but doesn't feed the result back to the security screen's list. That predates this PR, so I'll track it separately. deletePassword has no prebuilt UI caller, so the eager Retrofit validation test is its proof.

@swolfand swolfand left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Both fixes are correct, and the Retrofit-backed tests fail without them. LGTM.

@mikepitre
mikepitre merged commit 3e598e4 into main Oct 1, 2026
10 checks passed
@mikepitre
mikepitre deleted the mike/fix-retrofit-service-methods branch October 1, 2026 19:41
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