feat: changePassword + useLukkChangePassword - #46
Merged
Merged
Conversation
Client support for lukk's new `POST /auth/password` (stsepelin/lukk#27), so the feature isn't server-only. `ChangePasswordInput` deliberately has no token field: the reset flow proves control of an email address, this one proves knowledge of the existing password, and that difference is the entire reason the endpoint is safe to expose to a session. A stolen access token alone must not be enough to take an account over permanently. The composable is thin on purpose. lukk revokes every OTHER session and keeps the current one, so there is no token to swap and nothing to re-login — the existing state is already correct afterwards. A composable that cleared session state here would log the user out of the tab they just changed their password in, which is why one of the tests asserts the access state is untouched rather than merely that the call was made.
… key
Review points, both fair.
`useLukkPassword` reads as an umbrella for password operations, which invites a
reader to expect `sendResetLink` on it too — and `useLukkPasswordReset` then
reads as a subset of it. Named for the one operation it performs instead. Free
to do now: the changeset is pending, so nothing has been published under the old
name.
The test re-typed the `'lukk:access'` literal, which CLAUDE.md explicitly forbids
— shared state keys live in `runtime/keys.ts` so a rename can't silently
decouple a test from the thing it is asserting about. Imports `ACCESS_KEY` now,
like its siblings.
Also returns `{ changing, changePassword }` rather than fn-first, matching
`useLukkPasswordReset`'s shape — the two are read side by side in the docs.
stsepelin
added a commit
to stsepelin/lukk-docs
that referenced
this pull request
Aug 21, 2026
The page said the client had no composable and showed a `useLukkFetch` call — true when written, not any more (stsepelin/lukk-js#46). Documents the composable and keeps a `useLukkForm` example, since the 422 validation bag is the thing most callers actually need to wire up. Two corrections from the security review of the server side: A success releases the `login` counter as well as `confirm`. Without that, a user who was being brute-forced, noticed, and changed their password stayed locked out of login everywhere else — the page now says so, because "changing your password fixes it" is exactly what a reader would otherwise assume. The session sweep is not unconditional: a token carrying no `fid` identifies no session to preserve, so every session is revoked rather than none. The page described only the common case.
Raised as a loading-flag bug: two overlapping calls, the first to settle clears `changing` while the second is pending, re-enabling a bound submit button. That is mechanically true, and the suggested fix was an in-flight counter. The counter treats the symptom. I checked what an overlap actually costs by driving the real endpoint twice, and it is not a cosmetic problem: first: 200 second (same body): 422 confirm lockout attempts burned: 1 The second request carries a `current_password` the first has already replaced, so lukk reads it as a wrong password and spends one of the account's consecutive-failure attempts. A double-submit quietly eats the user's lockout budget and reports a 422 for a change that had in fact just succeeded. A counter keeps the button disabled and still lets that request go. So the overlap is refused outright, with no request made. That also makes `changing` correct by construction — there is never more than one in flight, so a single boolean cannot go stale, and no counter is needed. Deliberately not propagated to the sibling composables. The pattern appears in six of them, but a reset link or a verification resend is idempotent and verifies no secret, so a duplicate there costs nothing; adding re-entry guards everywhere would be ceremony. This endpoint is different because it checks a credential against a cap. Both tests were mutation-tested: removing the guard makes the first fail. The second exists because a guard that never releases would wedge the composable for a user who simply mistyped their current password.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Client support for lukk's new
POST /auth/password(stsepelin/lukk#27), so the feature isn't server-only.ChangePasswordInputdeliberately has no token field. The reset flow proves control of an email address; this one proves knowledge of the existing password — and that difference is the entire reason it's safe to expose to a session. A stolen access token alone must not be enough to take an account over permanently.The composable is thin on purpose
lukk revokes every other session and keeps the current one, so there's no token to swap and nothing to re-login — existing state is already correct afterwards. One test asserts the access state is untouched rather than merely that the call was made, because a composable that cleared state here would log the user out of the tab they just changed their password in.
From review
useLukkPassword→useLukkChangePassword. The former reads as an umbrella covering the reset flow too, which would makeuseLukkPasswordResetlook like a subset of it. Free to change now — the changeset is pending, so nothing shipped under the old name.'lukk:access'literal, whichCLAUDE.mdexplicitly forbids: shared state keys live inruntime/keys.tsso a rename can't silently decouple a test from what it asserts about. ImportsACCESS_KEYnow.{ changing, changePassword }(refs first) to matchuseLukkPasswordReset's shape — they're read side by side in the docs.Verification
91 + 338tests, 100% coverage both packages, lint + typecheck + build clean.Greptile Summary
Adds authenticated password-change support to the core client and a Nuxt composable that exposes request progress without altering the surviving session state.
POST /auth/passwordrequests through the core client.useLukkChangePasswordwith loading-state and error propagation behavior.Confidence Score: 4/5
The overlapping-call loading-state defect should be fixed before merging so duplicate password-change requests cannot re-enable submission while work remains pending.
Each invocation clears the same
changingref independently, allowing an earlier request to report an idle state while another password-change request is still running.Files Needing Attention: packages/nuxt/src/runtime/composables/useLukkChangePassword.ts
Important Files Changed
ChangePasswordInputcontract, exported through the existing wildcard entrypoint.Sequence Diagram
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "refactor: rename to useLukkChangePasswor..." | Re-trigger Greptile