fix(rules): allow completedByUsers on session update so finished games close (B-001 P1)
The sessions allow-update rule required affectedKeys().hasOnly(['status','completedAt']), but the async-game completion path (markUserComplete) always writes completedByUsers, so every 'I reached results' write was denied and the session stayed active forever -> the couple was locked out of starting any new game (only the destructive 'End their game' worked, since abandonSession only diffs status/completedAt). Rule now permits ['status','completedAt','completedByUsers'], lets any couple member record completion progress, keeps startedByUserId immutable and status monotonic (active->completed). Deployed + verified live: both finish a game -> session auto-completes (completedByUsers =[both]) -> next game starts immediately (no 'Waiting for partner' block). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ebd3b2ed1f
commit
a82c43ad90
|
|
@ -309,17 +309,20 @@ service cloud.firestore {
|
|||
allow create: if isCouplesMember(coupleId)
|
||||
&& request.resource.data.startedByUserId == request.auth.uid;
|
||||
|
||||
// Update: only the user who started the session can update it, OR valid status transitions.
|
||||
// startedByUserId is immutable for direct client writes.
|
||||
// Update: any couple member may record session progress/completion.
|
||||
// (Async two-device games mark each player done via `completedByUsers`; the
|
||||
// session flips active→completed once both are in. The previous rule only
|
||||
// allowed `status`/`completedAt`, so every `completedByUsers` write was denied
|
||||
// and finished games never closed — locking the couple out of new games. B-001.)
|
||||
allow update: if isCouplesMember(coupleId)
|
||||
// Either the original starter can update
|
||||
&& (resource.data.startedByUserId == request.auth.uid
|
||||
// Or status transition is valid: active → completed
|
||||
|| (resource.data.status == 'active' && request.resource.data.status == 'completed'))
|
||||
// startedByUserId cannot be changed by clients
|
||||
// startedByUserId is immutable for direct client writes.
|
||||
&& request.resource.data.startedByUserId == resource.data.startedByUserId
|
||||
// Only a fixed set of fields may change
|
||||
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['status', 'completedAt']);
|
||||
// Only session progress/completion fields may change.
|
||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||
.hasOnly(['status', 'completedAt', 'completedByUsers'])
|
||||
// status is monotonic: stay the same, or transition active → completed (never revert).
|
||||
&& (request.resource.data.status == resource.data.status
|
||||
|| (resource.data.status == 'active' && request.resource.data.status == 'completed'));
|
||||
|
||||
// Delete: server-only (admin SDK). Admin SDK bypasses rules.
|
||||
allow delete: if false;
|
||||
|
|
|
|||
Loading…
Reference in New Issue