Skip to content

Purchase completion opens the AI gate from a session captured before the StoreKit await #51

Description

@tabmail-kmyi

Summary

Purchase completion opens the process-global AI gate based on a session that was captured before the StoreKit await. If the signed-in account changes during that window, the gate can be opened while a different account is current. This is the purchase-path sibling of the restore-path ownership guard added in #50 (which was intentionally scoped to the restore path only).

Mechanism

PlanPickerView.purchaseProduct(_:):

  1. captures session = TabMailAuthService.getSession() before await storeKit.purchase(...),
  2. after the await resolves, calls AISubscriptionGate.shared.openGate().

AISubscriptionGate is process-global, not per-account. The transaction is correctly stamped with the purchaser's appAccountToken, but the local gate is opened for whichever account is current when the await resolves. If the app switched from account A → B during the purchase, B's gate opens. It self-heals on B's next authoritative whoami (AISubscriptionGate.apply() closes on has_subscription:false).

Proposed fix (common-path-safe)

At purchase completion, compare the current TabMailAuthService.getSession()?.userId to the captured purchaser id; do not open the gate if they differ.

Acceptance

Why deferred

Degenerate and self-healing; no user-visible harm beyond a transient wrong-open that the next revalidation corrects. Tracked for purchase-path parity with #50, to be picked up when convenient. Related: #50 (restore-path guard).

Metadata

Metadata

Assignees

No one assigned

    Labels

    deferredOpen work intentionally deferred by owner decision

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions