Registration: a refused submit fetches a fresh form before the next one #32

Closed
ann wants to merge 1 commit from fix/registration-captcha into master
Owner

Fixes #31

The error alert's OK returned to the same registration form after the server refused a submit. ejabberd deletes the XEP-0158 challenge on the first check whatever the verdict, so submitting that form again always failed with "The CAPTCHA verification has failed" and hid the real reason ("User already exists", one registration per IP per 600 s on another.im, …).

  • RegistrationViewModel: an <Error> that answers a submit (phase == .submitting) sets formSpent. dismissError() then calls retry() (fresh register connect with the same token, typed answers put back) instead of going back to .ready on the spent form. connect() and cancel() clear the flag.
  • No other change. The embedded libtacky (v1.1.15) is fine: what the app sends is the same as anotherim-js.

Testing. I checked the behaviour on libtacky's ejabberd 26.09 rig (captcha_pow only, as production), driving register the way this view model does:

  • taken name, then the same form → The CAPTCHA verification has failed;
  • taken name, then a fresh connect → <Success>;
  • plain flow → <Success>;
  • 125 s wait → <Success>.

This Swift change was not compiled or run. There is no Xcode on the machine where it was written. It is small and reads as type-correct against the surrounding code, but please build it and try it on a device: register with a taken name, tap OK, change the name, then Register. It should succeed, or show the server's real reason.

🤖 Generated with Claude Code

Fixes #31 The error alert's *OK* returned to the same registration form after the server refused a submit. ejabberd deletes the XEP-0158 challenge on the first check whatever the verdict, so submitting that form again always failed with "The CAPTCHA verification has failed" and hid the real reason ("User already exists", one registration per IP per 600 s on another.im, …). - `RegistrationViewModel`: an `<Error>` that answers a submit (`phase == .submitting`) sets `formSpent`. `dismissError()` then calls `retry()` (fresh `register connect` with the same token, typed answers put back) instead of going back to `.ready` on the spent form. `connect()` and `cancel()` clear the flag. - No other change. The embedded libtacky (v1.1.15) is fine: what the app sends is the same as anotherim-js. **Testing.** I checked the behaviour on libtacky's ejabberd 26.09 rig (captcha_pow only, as production), driving `register` the way this view model does: - taken name, then the same form → `The CAPTCHA verification has failed`; - taken name, then a fresh connect → `<Success>`; - plain flow → `<Success>`; - 125 s wait → `<Success>`. **This Swift change was not compiled or run.** There is no Xcode on the machine where it was written. It is small and reads as type-correct against the surrounding code, but please build it and try it on a device: register with a taken name, tap OK, change the name, then Register. It should succeed, or show the server's real reason. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The user (2026-11-07): sign-up on iOS ends in "The CAPTCHA verification has
failed". ejabberd 26.09 checks the XEP-0158 challenge before anything else
and deletes it whatever the verdict (ejabberd_captcha check_captcha_pow), so
once a submit is refused for another reason ("User already exists", one
registration per IP per 600 s on another.im, a malformed name) the form
behind it is spent. The error alert's OK put the view model back to .ready
on that same form, and the next Create sent it again: libtacky solved a new
proof of work for a challenge the server no longer has, and every further
attempt got the CAPTCHA text instead of the real reason. Reproduced on
libtacky's ejabberd rig driving register exactly as this view model does.

A submit's <Error> now marks the form spent, and dismissing the error
fetches a fresh form (retry: same token, answers typed kept), as the
alert's Retry already did. Not compiled here (no Xcode on this machine).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor

Correction to what I wrote here at first: feat/ui-redesign does fail with this text, from another cause. Its RegistrationModel.submit sent every hidden field since 5b49046e, SHA-256 among them; libtacky hands the proof-of-work field out as hidden and solves it only when that key is not among the values (register.tcl Submit), so on another.im (captcha_pow) every submit, the first included, went with an empty hash and got "The CAPTCHA verification has failed", and Try again failed the same way. (5b49046e was checked on conversations.im, which offers only the picture.) Fix for feat/ui-redesign in branch fix/registration-pow (dc5b0cf): https://dev.another.im/another-im/anotherim-swift/compare/feat/ui-redesign...fix/registration-pow — the submit sends only what the person answers, as anotherim-js and master do. This PR's change (OK resubmitting a spent form) is master's old RegistrationViewModel only, which is why it stays closed.

Correction to what I wrote here at first: `feat/ui-redesign` does fail with this text, from another cause. Its `RegistrationModel.submit` sent every hidden field since 5b49046e, `SHA-256` among them; libtacky hands the proof-of-work field out as hidden and solves it only when that key is not among the values (`register.tcl` Submit), so on another.im (`captcha_pow`) every submit, the first included, went with an empty hash and got "The CAPTCHA verification has failed", and *Try again* failed the same way. (5b49046e was checked on conversations.im, which offers only the picture.) Fix for `feat/ui-redesign` in branch `fix/registration-pow` (dc5b0cf): https://dev.another.im/another-im/anotherim-swift/compare/feat/ui-redesign...fix/registration-pow — the submit sends only what the person answers, as anotherim-js and master do. This PR's change (OK resubmitting a spent form) is `master`'s old `RegistrationViewModel` only, which is why it stays closed.
fmodf closed this pull request 2026-11-07 08:00:32 +00:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
done
fixed
returned
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/anotherim-swift!32
No description provided.