Skip to content

feat(subplat): add support for free access program#20764

Open
StaberindeZA wants to merge 8 commits into
mainfrom
pay-3780-add-free-access-program
Open

feat(subplat): add support for free access program#20764
StaberindeZA wants to merge 8 commits into
mainfrom
pay-3780-add-free-access-program

Conversation

@StaberindeZA

Copy link
Copy Markdown
Contributor

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 pull request

  • 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.

Issue that this pull request solves

Closes: PAY-3780

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on:
  • Suggested review order:
  • Risky or complex parts:

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.

@StaberindeZA StaberindeZA force-pushed the pay-3780-add-free-access-program branch 2 times, most recently from dcc4724 to 7f10a3f Compare June 22, 2026 15:29
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
@StaberindeZA StaberindeZA force-pushed the pay-3780-add-free-access-program branch from 43ed500 to d30c6c2 Compare June 23, 2026 21:25
@StaberindeZA StaberindeZA marked this pull request as ready for review June 25, 2026 00:47
@StaberindeZA StaberindeZA requested review from a team as code owners June 25, 2026 00:47
Copilot AI review requested due to automatic review settings June 25, 2026 00:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-program library that projects Strapi access entries 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 a hasFreeAccess signal 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 thread apps/payments/next/.env
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[];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants