feat(subplat): add support for free access program#20764
Open
StaberindeZA wants to merge 8 commits into
Open
Conversation
dcc4724 to
7f10a3f
Compare
Because - Transfer ownership of the Mozilla VPN Free Access program to the SubPlat and EntPlat teams, which aligns with the goal of evolving the subscirption platform beyond subscriptions. - Allow other Mozilla services to also utilize the Free Access Program. This commit - Adds support for the Free Access Program, by broadcasting capabilities for enabled customers, even if they don't have a subscription. - Read list of emails from Strapi and what capabilities should be provided to these users. - On auth-server /profile query, return the relevant capability if the user email is in the list configured in Strapi. - Add webhook listener to `payments-api` that listens for changes in Strapi and then calls an API in auth-server, which broadcasts capability added or removed via event-broker to RPs. - Show Paid Subscriptions option to user that have access via a B2B subscription. On the subscription management page added a new section indicating services provided by the customers organization. Closes PAY-3780
43ed500 to
d30c6c2
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a “Free Access Program” path to SubPlat/EntPlat so capabilities can be granted to allowlisted users (via Strapi) even when they have no paid subscription, and surfaces that state across auth-server, Settings, and the payments subscription-management UI.
Changes:
- Introduces a new
@fxa/free-access-programlibrary that projects Strapiaccessentries into a cached snapshot, and provides a reconciler that diffs state and emits capability deltas. - Adds auth-server support: merges free-access capabilities into
profile:subscriptions, adds ahasFreeAccesssignal to/account, and exposes a Strapi webhook endpoint + periodic reconcile script. - Updates Settings and payments-next UI to display/manage “free access” experiences (nav link gating + subscription-management cards), including new validators and session email plumbing.
Reviewed changes
Copilot reviewed 100 out of 103 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.base.json | Adds TS path alias for @fxa/free-access-program. |
| packages/fxa-settings/src/models/contexts/AppContext.ts | Adds hasFreeAccess default value to Settings app context. |
| packages/fxa-settings/src/models/contexts/AccountStateContext.tsx | Extends account state with hasFreeAccess and mapping from unified data. |
| packages/fxa-settings/src/models/Account.ts | Adds hasFreeAccess to account model + getter. |
| packages/fxa-settings/src/lib/hooks/useAccountData.ts | Maps /account response hasFreeAccess into Settings state. |
| packages/fxa-settings/src/lib/account-storage.ts | Persists hasFreeAccess in account storage/state selectors. |
| packages/fxa-settings/src/components/Settings/Nav/index.tsx | Shows subscriptions entry point when user has paid subs or free access. |
| packages/fxa-settings/src/components/Settings/Nav/index.test.tsx | Adds test for subscriptions link visibility for allowlisted users. |
| packages/fxa-auth-server/test/support/jest-setup-env.ts | Loads reflect-metadata earlier for integration test harness. |
| packages/fxa-auth-server/test/remote/subscription_tests.in.spec.ts | Ensures CMS config has placeholder Strapi client values for tests. |
| packages/fxa-auth-server/scripts/free-access-program-reconcile.ts | Adds auth-server cron-style reconcile script entrypoint. |
| packages/fxa-auth-server/scripts/convert-customers-to-stripe-automatic-tax.ts | Loads reflect-metadata to avoid decorator metadata crashes in tests. |
| packages/fxa-auth-server/lib/routes/subscriptions/index.ts | Registers free-access Strapi webhook routes when reconciler is wired. |
| packages/fxa-auth-server/lib/routes/subscriptions/free-access-program-webhook.ts | Implements Strapi webhook handler with replay dedupe + reconcile dispatch. |
| packages/fxa-auth-server/lib/routes/subscriptions/free-access-program-webhook.spec.ts | Unit tests for webhook handler behavior and dedupe. |
| packages/fxa-auth-server/lib/routes/password.spec.ts | Updates mocks (TypeScript-safe mocks for logger/statsd). |
| packages/fxa-auth-server/lib/routes/account.ts | Adds free-access capability merge + hasFreeAccess flag in /account. |
| packages/fxa-auth-server/lib/routes/account.spec.ts | Updates tests to provide CapabilityService.hasFreeAccess mock shape. |
| packages/fxa-auth-server/lib/payments/processing-tasks-setup.ts | Loads reflect-metadata for processing-task scripts. |
| packages/fxa-auth-server/lib/payments/free-access-program-setup.ts | Wires Free Access manager + reconciler into TypeDI/Nest-ish deps. |
| packages/fxa-auth-server/lib/payments/free-access-in-process-notifier.ts | New notifier that resolves email→uid and calls capability fanout logic in-process. |
| packages/fxa-auth-server/lib/payments/free-access-in-process-notifier.spec.ts | Unit tests for notifier behavior and error handling. |
| packages/fxa-auth-server/lib/payments/capability.ts | Merges free-access capabilities; adds hasFreeAccess and processEmailListChange; safer empty-list short-circuit. |
| packages/fxa-auth-server/lib/payments/capability.spec.ts | Adds coverage for new merge and processEmailListChange behavior. |
| packages/fxa-auth-server/lib/oauth/grant.spec.ts | Ensures subscriptions enabled in config mock for grant tests. |
| packages/fxa-auth-server/lib/oauth/grant.js | Gates profile:subscriptions token enrichment on subscriptions-enabled. |
| packages/fxa-auth-server/jest.setup.js | Loads reflect-metadata for unit tests. |
| packages/fxa-auth-server/jest.config.js | Adds Jest moduleNameMapper for @fxa/free-access-program. |
| packages/fxa-auth-server/config/index.ts | Adds Free Access Program config + Strapi webhook secret config. |
| packages/fxa-auth-server/bin/key_server.js | Wires Free Access manager/reconciler into auth-server boot. |
| libs/shared/db/firestore/src/index.ts | Re-exports Firestore config types for consumers via package entrypoint. |
| libs/shared/cms/src/lib/types.ts | Adds FreeAccessCardContent type for subscription-management free access cards. |
| libs/shared/cms/src/lib/strapi.client.ts | Hardens webhook signature verification (prefix + length check). |
| libs/shared/cms/src/lib/strapi.client.spec.ts | Adds tests for webhook signature verification behavior. |
| libs/shared/cms/src/lib/strapi.client.config.ts | Allows localhost-like URIs (require_tld: false). |
| libs/shared/cms/src/lib/queries/accesses/query.ts | Adds Strapi GraphQL query to fetch accesses (matchers + offerings + capabilities). |
| libs/shared/cms/src/lib/queries/accesses/index.ts | Exports accesses query + factories + result type. |
| libs/shared/cms/src/lib/queries/accesses/factories.ts | Adds test factories for accesses query result shapes. |
| libs/shared/cms/src/lib/product-configuration.manager.ts | Adds helper to hydrate free-access cards for subscription-management UI. |
| libs/shared/cms/src/lib/product-configuration.manager.spec.ts | Tests for free-access card hydration and error handling. |
| libs/shared/cms/src/index.ts | Re-exports the new accesses query module. |
| libs/shared/cms/src/generated/graphql.ts | Updates generated GraphQL types to include Access/FreeAccessProgram. |
| libs/shared/cms/src/generated/gql.ts | Updates generated gql map to include the new Accesses query. |
| libs/payments/webhooks/src/lib/cms-webhooks.controller.spec.ts | Formatting import change (no behavior change). |
| libs/payments/ui/src/lib/nestapp/validators/GetSubManPageContentActionResult.ts | Adds freeAccess response validation shape. |
| libs/payments/ui/src/lib/nestapp/validators/GetSubManPageContentActionArgs.ts | Adds optional email argument for subscription-management page content. |
| libs/payments/ui/src/lib/nestapp/nextjs-actions.service.ts | Passes email through to subscription-management service. |
| libs/payments/ui/src/lib/nestapp/config.ts | Adds Free Access Program config block to payments UI RootConfig. |
| libs/payments/ui/src/lib/nestapp/app.module.ts | Registers FreeAccessProgramManager in Nest module providers. |
| libs/payments/ui/src/lib/client/components/FreeAccessContent/index.tsx | New client component rendering a free-access “included via org” card. |
| libs/payments/ui/src/lib/client/components/FreeAccessContent/index.test.tsx | Unit tests for FreeAccessContent rendering. |
| libs/payments/ui/src/lib/client/components/FreeAccessContent/en.ftl | New FTL string for the free-access card messaging. |
| libs/payments/ui/src/lib/actions/getSubManPageContent.ts | Adds session email to action call to hydrate free-access cards. |
| libs/payments/ui/src/index.ts | Exports FreeAccessContent component. |
| libs/payments/ui-auth/src/lib/session.ts | Adds getSessionEmail() helper. |
| libs/payments/ui-auth/src/lib/session.spec.ts | Tests for getSessionEmail(). |
| libs/payments/ui-auth/src/index.ts | Exports getSessionEmail. |
| libs/payments/management/src/lib/types.ts | Adds FreeAccessContent type for management response. |
| libs/payments/management/src/lib/subscriptionManagement.service.ts | Resolves free-access grants via manager + CMS card hydration and excludes paid offerings. |
| libs/payments/management/src/lib/subscriptionManagement.service.spec.ts | Adds unit coverage for free-access card behavior. |
| libs/payments/management/src/lib/subscriptionManagement.service.in.spec.ts | Ensures freeAccess field present in integration result shape. |
| libs/free-access-program/tsconfig.spec.json | New library test TS config. |
| libs/free-access-program/tsconfig.lib.json | New library build TS config. |
| libs/free-access-program/tsconfig.json | New library root TS config with references. |
| libs/free-access-program/src/lib/util/projectAccess.ts | Projects normalized Strapi access data into per-email entitlement records. |
| libs/free-access-program/src/lib/util/projectAccess.spec.ts | Unit tests for projection behavior and skip reasons. |
| libs/free-access-program/src/lib/util/parseStrictDate.ts | Parses strict YYYY-MM-DD to “start of next day UTC” expiry. |
| libs/free-access-program/src/lib/util/parseStrictDate.spec.ts | Unit tests for date parsing edge cases. |
| libs/free-access-program/src/lib/util/parseMatcherValue.ts | Parses Strapi [date, description] tuple matcher values. |
| libs/free-access-program/src/lib/util/parseMatcherValue.spec.ts | Unit tests for matcher parsing. |
| libs/free-access-program/src/lib/util/normalizeGraphQLAccess.ts | Normalizes AccessesQuery rows into a source-agnostic projection input. |
| libs/free-access-program/src/lib/util/normalizeGraphQLAccess.spec.ts | Unit tests for normalization behavior. |
| libs/free-access-program/src/lib/util/flattenOfferingCapabilities.ts | Flattens capabilities across offerings. |
| libs/free-access-program/src/lib/util/flattenOfferingCapabilities.spec.ts | Tests for capability flattening behavior. |
| libs/free-access-program/src/lib/util/flattenCapabilities.ts | Flattens clientId→capabilities map to a set of slugs for diffing. |
| libs/free-access-program/src/lib/util/flattenCapabilities.spec.ts | Tests for map flattening behavior. |
| libs/free-access-program/src/lib/util/filterByEntitlement.ts | Filters snapshot records by entitlement id. |
| libs/free-access-program/src/lib/util/filterByEntitlement.spec.ts | Tests for entitlement filtering. |
| libs/free-access-program/src/lib/util/diffCapabilities.ts | Diffs capability maps at slug level. |
| libs/free-access-program/src/lib/util/diffCapabilities.spec.ts | Tests for capability diffing. |
| libs/free-access-program/src/lib/util/collectOfferingApiIdentifiers.ts | Collects offering apiIdentifiers from linked offerings. |
| libs/free-access-program/src/lib/util/collectOfferingApiIdentifiers.spec.ts | Tests for offering id collection. |
| libs/free-access-program/src/lib/util/collectCapabilityMap.ts | Builds clientId→capability list from Strapi capability/service refs. |
| libs/free-access-program/src/lib/util/collectCapabilityMap.spec.ts | Tests for capability map collection behavior. |
| libs/free-access-program/src/lib/util/buildSnapshotKey.ts | Defines composite snapshot key format. |
| libs/free-access-program/src/lib/util/buildSnapshotKey.spec.ts | Tests for snapshot key building. |
| libs/free-access-program/src/lib/free-access-program.types.ts | Introduces shared types + notifier token contract. |
| libs/free-access-program/src/lib/free-access-program.reconciler.service.ts | Reconciler service that diffs snapshots and fans out changes. |
| libs/free-access-program/src/lib/free-access-program.reconciler.service.spec.ts | Unit tests for reconciler behavior, metrics, and error handling. |
| libs/free-access-program/src/lib/free-access-program.manager.ts | Manager that fetches Strapi accesses and caches the projected snapshot. |
| libs/free-access-program/src/lib/free-access-program.manager.spec.ts | Unit tests for manager read/query behavior. |
| libs/free-access-program/src/lib/free-access-program.factories.ts | Test factories for the new library’s domain types. |
| libs/free-access-program/src/lib/free-access-program.client.config.ts | Config class + mock provider for the new library. |
| libs/free-access-program/src/index.ts | Library public exports. |
| libs/free-access-program/README.md | Initial documentation for the new library (currently inconsistent with implementation). |
| libs/free-access-program/project.json | Nx project configuration for the new library. |
| libs/free-access-program/package.json | Declares @fxa/free-access-program package metadata. |
| libs/free-access-program/jest.config.ts | Jest config for the new library. |
| libs/free-access-program/.swcrc | SWC config enabling decorators metadata for tests/build. |
| apps/payments/next/app/[locale]/subscriptions/manage/page.tsx | Renders free-access cards section on subscription management page. |
| apps/payments/next/app/[locale]/subscriptions/manage/en.ftl | Adds localized strings for free-access section headings/aria labels. |
| apps/payments/next/.env | Adds Free Access Program client config env vars (one typo noted). |
| apps/payments/api/src/config/index.ts | Switches FirestoreConfig import to package export path. |
Files not reviewed (2)
- libs/shared/cms/src/generated/gql.ts: Generated file
- libs/shared/cms/src/generated/graphql.ts: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+104
to
+107
| FREE_ACCESS_PROGRAM_CLIENT_CONFIG__FIRESTORE_CACHE_COLLECTION_NAME=subplat-free-access-program-cache | ||
| FREE_ACCESS_PROGRAM_CLIENT_CONFIG__MEM_CACHE_T_T_L=300 # 5 minutes | ||
| FREE_ACCESS_PROGRAM_CLIENT_CONFIG__FIRESTORE_CACHE_TTL=1800 # 30 minutes | ||
| FREE_ACCESS_PROGRAM_CLIENT_CONFIG__FIRESTORE_OFFLINE_CACHE_TTL=604800 # 7 days |
Comment on lines
+2467
to
+2472
| webhookSecret: { | ||
| default: 'PLACEHOLDER', | ||
| doc: 'Strapi client webhook secret', | ||
| env: 'STRAPI_CLIENT_WEBHOOK_SECRET', | ||
| format: String, | ||
| }, |
Comment on lines
+121
to
+130
| private markSeen(key: string): void { | ||
| const now = Date.now(); | ||
| // Bound the map size by sweeping expired entries when we hit the cap. | ||
| if (this.seenEvents.size >= DEDUPE_MAX_ENTRIES) { | ||
| for (const [k, exp] of this.seenEvents) { | ||
| if (exp <= now) this.seenEvents.delete(k); | ||
| } | ||
| } | ||
| this.seenEvents.set(key, now + DEDUPE_TTL_MS); | ||
| } |
Comment on lines
+295
to
+299
| <li | ||
| key={`${grant.offeringApiIdentifier}-${index}`} | ||
| aria-labelledby={`${grant.offeringApiIdentifier}-free-access-information`} | ||
| className="leading-6 pb-4 last:pb-0" | ||
| > |
Comment on lines
+3
to
+12
| Firestore-backed projection of Strapi access records that grant | ||
| free access to RP services. Each document carries a per-(email, entitlement) | ||
| `expiresAt` enforced by a Firestore TTL policy; when a document is removed | ||
| (TTL reap or manual delete), the resulting Eventarc `onDelete` event fans out | ||
| a `subscription:update` notification so consumers re-resolve the user's | ||
| capabilities. | ||
|
|
||
| This library owns the read/write data layer only. The Strapi reconciler and | ||
| the Eventarc → SNS notifier are wired up in the consuming service | ||
| (`payments-api`). |
Comment on lines
+234
to
+236
| @ValidateNested() | ||
| @Type(() => FreeAccess) | ||
| freeAccess!: FreeAccess[]; |
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.
Because
This pull request
payments-apithat listens for changes in Strapi and then calls an API in auth-server, which broadcasts capability added or removed via event-broker to RPs.Issue that this pull request solves
Closes: PAY-3780
Checklist
Put an
xin the boxes that applyHow to review (Optional)
Screenshots (Optional)
Please attach the screenshots of the changes made in case of change in user interface.
Other information (Optional)
Any other information that is important to this pull request.