Feat/money account subscription controller - #10017
Conversation
…tionController with JWT handling
…s and clean up unused types
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4691a6f. Configure here.
| } | ||
|
|
||
| this.clearState(); | ||
| } |
There was a problem hiding this comment.
Force refresh signs the user out
High Severity
#forceBearerTokenRefresh calls AuthenticationController:performSignOut to rotate the JWT, including on the 60s poll. That clears global auth session data and publishes a real sign-out. This controller then always clearState(), which drops entitlements and stops polling via #syncPollingState. If getBearerToken fails, the user stays signed out, selectors stay fail-closed, and the refresh loop does not restart.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 4691a6f. Configure here.
There was a problem hiding this comment.
this only clear the access token from the authentication controller, next time the access token is requested, a new access token will be fetched.
| } | ||
|
|
||
| this.#moneyAccountSubscriptionSnapshot = nextSnapshot; | ||
| await this.forceRefresh(); |
There was a problem hiding this comment.
Throttled subscription refresh is dropped
Medium Severity
#handleSubscriptionStateChange writes #moneyAccountSubscriptionSnapshot before forceRefresh(). If that refresh is throttled or coalesced onto an in-flight run, the newer snapshot is recorded without a later refresh. The next identical event is treated as unchanged, so a subscribe, cancel, or status change can leave JWT entitlements stale until another distinct snapshot arrives.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4691a6f. Configure here.


Explanation
References
Checklist
Note
Medium Risk
New subscription gating and JWT refresh logic (including performSignOut during force refresh) can affect fee waivers and feature access if claims are wrong or races mishandled; extensive tests mitigate but consumers must wire the controller correctly.
Overview
Introduces
@metamask/money-account-subscription-controller, a read-only controller that derives Money Account Plus plan, entitlement flags, and subscriber status from the Profile JWT (productEntitlements.moneyAccountPlus), with JWT helpers and selectors for subscriber/entitlement/usage gating.MoneyAccountSubscriptionControllerhydrates on sign-in and bootstrap (persisted state is cleared until re-validated), clears on sign-out, and reacts toSubscriptionController:stateChangedformoney_account_pluslifecycle changes.forceRefreshcoordinates sign-out/sign-in viaAuthenticationController:performSignOutand a fresh bearer token, with throttling and queueing so stale async results do not overwrite newer auth. While a trading surface is active (startEntitlementRefresh/stopEntitlementRefresh), entitled signed-in users get 60s polling that triggers the same forced JWT refresh path.Monorepo updates register the package under Web3Auth / team-shield in
codeowners.ts,CODEOWNERS,teams.json, roottsconfigreferences,READMEpackage list and dependency graph, andyarn.lock.Reviewed by Cursor Bugbot for commit 4691a6f. Bugbot is set up for automated code reviews on this repo. Configure here.