Repository navigation
[Mobile] Registration and invite acceptance - #22
Merged
Merged
Conversation
Closes #3. The invitation screen sits outside both route groups, guarded by neither: most people opening one of these have no account yet, and bouncing them to a login screen without saying what they were invited to is how an invitation gets ignored. It mirrors the web keeping /invite/:token outside RequireAuth. One thing the web gets for free that this cannot: on the web the page's own origin IS the instance, while a softtrack:// link carries no host. Arriving with no known instance asks for sign-in first and comes back, rather than guessing. True universal links would need each self-hosted domain declared in the build, which is not possible for arbitrary hosts -- noted in the README rather than papered over. Registration derives its email from the invitation when there is one, because typing a different address would only earn a 403 on submit. The backend accepts an invite best-effort after creating the account and swallows failures, so the team list is re-read rather than assumed. The in-app banner is the only thing that tells an existing account an invitation exists, since nothing sends email. Verified against the reference stack, including the 422 whose `detail` is an array rather than a string -- the case the web's extractor drops. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Closes #3.
Stacked on #21 (teams), which is stacked on #20 (login). Review in that order; this PR's diff is only the registration and invitation work.
Ordered after teams on purpose: accepting an invitation has to set the active team, so it needs the team context to exist.
What changed
The invitation screen sits outside both route groups, guarded by neither. Most people opening one of these links have no account yet, and bouncing them to a login screen without saying what they were invited to is how an invitation gets ignored. Same reasoning as the web keeping
/invite/:tokenoutsideRequireAuth.Registration derives its email from the invitation when there is one, because typing a different address would only earn a 403 on submit. The backend accepts an invite best-effort after creating the account and swallows failures (
lib_identity/identity.py:144-150), so the team list is re-read rather than assumed.The in-app banner is the only thing that tells an existing account an invitation exists, since nothing sends email.
A limit worth stating plainly
On the web, an invite link's own origin is the instance. A
softtrack://link carries no host, and true universal links would require declaring each self-hosted domain in the build — which is impossible for arbitrary hosts.So:
softtrack://invite/<token>works, and an invitation arriving with no known instance asks you to sign in first and then returns to itself, rather than guessing. The issue's "(or store fallback)" seems to anticipate this, but it is a real constraint rather than something worked around. Documented in the README.Verification
73 unit tests plus 5 integration tests against a live instance: registration, the 422 whose
detailis an array rather than a string (the case the web's extractor drops), public invite preview while signed out, registration-by-invite, and the pending-invite banner path.The suite registers real accounts. Registration is throttled per IP and charged even on success, so running it repeatedly will 429 — the suite now uses two registrations per run instead of four, and reports the throttle with the fix (
docker compose restart backend) rather than a bare error.Not run on a device.