#120 — Einladungslink für bereits Mitglieder/Owner — Weiterleitung statt Fehler #123

Closed
opened 2026-08-18 13:14:17 +02:00 by lena · 3 comments
lena commented 2026-08-18 13:14:17 +02:00 (Migrated from git.butzei.de)

Story: Einladungslink für bereits Mitglieder/Owner — Weiterleitung statt Fehler

As a Nutzer, der einen alten oder erneut geöffneten Einladungslink zu einer Liste anklickt, in der
ich schon Mitglied oder Owner bin,
I want to direkt zur Liste weitergeleitet werden (mit einem Hinweis, dass ich schon dabei bin),
so that ich nicht mit einer generischen "Einladung ungültig"-Fehlermeldung hängen bleibe.

Background

Root Cause verifiziert im Code:

  • AcceptListInvitationCommandHandler.cs / AcceptShoppingListInvitationCommandHandler.cs selbst
    behandeln "schon Mitglied" bereits still und erfolgreich (isAlreadyMember-Check überspringt nur
    das erneute Anlegen der Mitgliedschaft, gibt aber trotzdem das DTO zurück).
  • Der eigentliche Fehler entsteht davor: CreateListInvitationCommandHandler.cs /
    CreateShoppingListInvitationCommandHandler.cs widerrufen beim Erzeugen eines neuen
    Einladungslinks automatisch jeden vorherigen aktiven Link für dieselbe Liste
    (existing.RevokedAt = DateTimeOffset.UtcNow). Ein alter, bereits genutzter oder erneut geteilter
    Link ist dann RevokedAt != nullAcceptListInvitationCommandHandler wirft
    UnauthorizedAccessException("Invalid or expired invitation link").
  • AcceptInvitePage.tsx fängt jeden Fehler außer "email unverified" in einem generischen
    state === 'error' ("Invitation invalid") auf — keine Sonderbehandlung für "ich bin eigentlich schon
    drin".

AC

  • Wenn das Akzeptieren eines Einladungslinks fehlschlägt (Token ungültig/abgelaufen/widerrufen), aber
    der eingeloggte Nutzer bereits Mitglied oder Owner der Ziel-Liste ist (aus dem Token selbst oder einer
    serverseitigen Zuordnung Token→Liste ableitbar, auch für einen inzwischen widerrufenen Token),
    navigiert die App zur entsprechenden Liste statt die generische Fehlerseite zu zeigen.
  • Beim Ankommen auf der Liste erscheint ein kurzer Hinweis (z. B. Toast/Popup): "Du bist bereits
    Mitglied dieser Liste."
  • Ist der Token tatsächlich ungültig/abgelaufen und der Nutzer kein Mitglied, bleibt die bisherige
    "Invitation invalid"-Fehlerseite unverändert bestehen.
  • Gilt für alle Listentypen mit Einladungslink (Todo-Listen, Einkaufslisten, Masterpacklisten).

Out of scope

  • Änderung des Revoke-bei-Neuerstellung-Verhaltens selbst (bleibt wie es ist — nur die Nutzer-Erfahrung
    beim Klick auf einen dadurch ungültig gewordenen Link wird verbessert).
# Story: Einladungslink für bereits Mitglieder/Owner — Weiterleitung statt Fehler **As a** Nutzer, der einen alten oder erneut geöffneten Einladungslink zu einer Liste anklickt, in der ich schon Mitglied oder Owner bin, **I want to** direkt zur Liste weitergeleitet werden (mit einem Hinweis, dass ich schon dabei bin), **so that** ich nicht mit einer generischen "Einladung ungültig"-Fehlermeldung hängen bleibe. ## Background Root Cause verifiziert im Code: - `AcceptListInvitationCommandHandler.cs` / `AcceptShoppingListInvitationCommandHandler.cs` selbst behandeln "schon Mitglied" bereits **still und erfolgreich** (`isAlreadyMember`-Check überspringt nur das erneute Anlegen der Mitgliedschaft, gibt aber trotzdem das DTO zurück). - Der eigentliche Fehler entsteht **davor**: `CreateListInvitationCommandHandler.cs` / `CreateShoppingListInvitationCommandHandler.cs` **widerrufen** beim Erzeugen eines neuen Einladungslinks automatisch jeden vorherigen aktiven Link für dieselbe Liste (`existing.RevokedAt = DateTimeOffset.UtcNow`). Ein alter, bereits genutzter oder erneut geteilter Link ist dann `RevokedAt != null` → `AcceptListInvitationCommandHandler` wirft `UnauthorizedAccessException("Invalid or expired invitation link")`. - `AcceptInvitePage.tsx` fängt jeden Fehler außer "email unverified" in einem generischen `state === 'error'` ("Invitation invalid") auf — keine Sonderbehandlung für "ich bin eigentlich schon drin". ## AC - Wenn das Akzeptieren eines Einladungslinks fehlschlägt (Token ungültig/abgelaufen/widerrufen), aber der eingeloggte Nutzer bereits Mitglied oder Owner der Ziel-Liste ist (aus dem Token selbst oder einer serverseitigen Zuordnung Token→Liste ableitbar, auch für einen inzwischen widerrufenen Token), navigiert die App zur entsprechenden Liste statt die generische Fehlerseite zu zeigen. - Beim Ankommen auf der Liste erscheint ein kurzer Hinweis (z. B. Toast/Popup): "Du bist bereits Mitglied dieser Liste." - Ist der Token tatsächlich ungültig/abgelaufen **und** der Nutzer kein Mitglied, bleibt die bisherige "Invitation invalid"-Fehlerseite unverändert bestehen. - Gilt für alle Listentypen mit Einladungslink (Todo-Listen, Einkaufslisten, Masterpacklisten). ## Out of scope - Änderung des Revoke-bei-Neuerstellung-Verhaltens selbst (bleibt wie es ist — nur die Nutzer-Erfahrung beim Klick auf einen dadurch ungültig gewordenen Link wird verbessert).
lena commented 2026-08-18 18:33:08 +02:00 (Migrated from git.butzei.de)

Claimed for this go-cycle (2026-08-18). Root cause and AC already scoped in the issue body: CreateListInvitationCommandHandler/CreateShoppingListInvitationCommandHandler revoke the previous active invitation link whenever a new one is created for the same list, so a reopened old link hits AcceptListInvitationCommandHandler's UnauthorizedAccessException even though the accept handler itself already treats "already a member" as a silent success. Plan: on accept failure, check server-side whether the current user is already a member/owner of the list the token maps to (even for a revoked token) and redirect to the list with a toast instead of the generic error page; applies to Todo lists, Shopping lists, and Master packing lists. Backend + frontend + tests.

Claimed for this go-cycle (2026-08-18). Root cause and AC already scoped in the issue body: CreateListInvitationCommandHandler/CreateShoppingListInvitationCommandHandler revoke the previous active invitation link whenever a new one is created for the same list, so a reopened old link hits AcceptListInvitationCommandHandler's UnauthorizedAccessException even though the accept handler itself already treats "already a member" as a silent success. Plan: on accept failure, check server-side whether the current user is already a member/owner of the list the token maps to (even for a revoked token) and redirect to the list with a toast instead of the generic error page; applies to Todo lists, Shopping lists, and Master packing lists. Backend + frontend + tests.
lena commented 2026-08-18 18:58:47 +02:00 (Migrated from git.butzei.de)

Pushed 5e318e1. Investigation found the story's own AC ("gilt fur alle Listentypen") was unreachable as originally scoped: AcceptInvitePage.tsx unconditionally called AcceptListInvitationCommand (Todo-only) regardless of token type, so Shopping/MasterPacking invite links were silently broken end-to-end (dead backend code paths, confirmed via grep - never called from the frontend). Fixed both issues together with a new AcceptInvitationCommandHandler that resolves the token against all three domains' invitation tables and either delegates to the existing per-type accept handler (still-active token, unchanged behavior) or returns AlreadyMember: true for a revoked/expired token when the user is already a member (the actual #120 fix), instead of throwing. Backend + frontend unit tests added for all three list types. Local build/tests green (Docker unavailable in this sandbox for the DB-backed handler tests - relying on real CI, run 308, to confirm those). Security review: same auth gates as before (authenticated + email-verified), no new information disclosure (generic error unchanged for non-members/nonexistent tokens, already-member response only echoes what the user already has access to).

Pushed 5e318e1. Investigation found the story's own AC ("gilt fur alle Listentypen") was unreachable as originally scoped: AcceptInvitePage.tsx unconditionally called AcceptListInvitationCommand (Todo-only) regardless of token type, so Shopping/MasterPacking invite links were silently broken end-to-end (dead backend code paths, confirmed via grep - never called from the frontend). Fixed both issues together with a new AcceptInvitationCommandHandler that resolves the token against all three domains' invitation tables and either delegates to the existing per-type accept handler (still-active token, unchanged behavior) or returns AlreadyMember: true for a revoked/expired token when the user is already a member (the actual #120 fix), instead of throwing. Backend + frontend unit tests added for all three list types. Local build/tests green (Docker unavailable in this sandbox for the DB-backed handler tests - relying on real CI, run 308, to confirm those). Security review: same auth gates as before (authenticated + email-verified), no new information disclosure (generic error unchanged for non-members/nonexistent tokens, already-member response only echoes what the user already has access to).
lena commented 2026-08-19 02:50:18 +02:00 (Migrated from git.butzei.de)

Code is already fully merged on master (commit 5e318e1, "#120: redirect stale-but-already-member invite links; fix Shopping/MasterPacking invite acceptance") from an earlier cycle - the issue was just never closed out. Verified: commit is an ancestor of current master HEAD, introduces a unified AcceptInvitationCommandHandler covering Todo/Shopping/MasterPacking invite tokens with the AlreadyMember: true redirect-with-toast behavior the AC calls for. No further work needed.

Closing as done.

Code is already fully merged on master (commit 5e318e1, "#120: redirect stale-but-already-member invite links; fix Shopping/MasterPacking invite acceptance") from an earlier cycle - the issue was just never closed out. Verified: commit is an ancestor of current master HEAD, introduces a unified `AcceptInvitationCommandHandler` covering Todo/Shopping/MasterPacking invite tokens with the `AlreadyMember: true` redirect-with-toast behavior the AC calls for. No further work needed. Closing as done.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
robert/todo#123
No description provided.