Developer Docs

Experiences Authorization

The Experiences subgraph utilizes a strict Role-Based Access Control (RBAC) model to secure all 13 of its capability modules. This ensures that sensitive mutations (e.g., configuring loyalty programs, managing warranties, checking in tickets) are protected against unauthorized access.

Architecture

Authorization within the Experiences subgraph relies on the @RequirePermission decorator provided by the common library. The system binds specific business operations to standard Permissions without needing to fetch organization memberships manually in every resolver.

Request Flow

  1. Gateway Parsing: The Gateway validates incoming JWTs and extracts the x-user-id header.
  2. CurrentActor Decorator: The @CurrentActor() decorator decodes the user payload injected by the gateway or extracts x-user-id to identify the requester.
  3. Guard Enforcement: The custom PermissionsGuard (implemented via @app/common and dynamically enforced) intercepts the GraphQL context.
  4. Permission Resolution: The guard extracts the x-org-id and queries the underlying MongoDB connection directly to verify the user's role against the required module permissions.

Module Permissions Matrix

Each Experiences module has been audited and secured using the following granular permissions:

Module / Operation Required Permission Description
Event Ticketing (createEvent, updateEvent) MANAGE_SERVICES Allows Malet owners to configure new events and capacities.
Event Check-in (checkInEventTicket) UPDATE_STATUS Allows staff to scan QR codes and update ticket states.
Loyalty Configuration (configureLoyalty) MANAGE_SERVICES Owners can enable/disable the point economy.
Loyalty Awards (awardLoyaltyPoints) MANAGE_ORDERS Authorizes manual point or achievement issuance.
Credit Wallet (addCredits, deductCredits) MANAGE_BILLING Protects atomic financial ledger mutations.
Warranty Management (submitWarrantyClaim) MANAGE_ORDERS Restricts users from altering established warranty periods.
Client Portal (createClientNote) MANAGE_USERS Protects private CRM interactions and advisor notes.

NOTE

Public queries (e.g., reading an active event catalog, fetching an artist profile) do not require permissions. Visitor-scoped mutations (e.g., redeemLoyaltyPoints, purchaseEventTicket) only require standard @CurrentActor() authentication to map actions to the visitor.


E2E Testing with Guards

To properly test resolvers secured by @RequirePermission in End-to-End (E2E) suites, you must simulate the Gateway's behavior by passing the user header:

const res = await request(app.getHttpServer())
  .post('/graphql')
  .send({
    query: `mutation { ... }`,
    variables: { ... },
  })
  // โœ… CORRECT: Simulate the gateway's parsed actor payload
  .set('user', JSON.stringify({ id: 'mock-owner-id' }));

CAUTION

Using .set('x-user-id', 'id') directly in subgraph E2E tests will result in a null actor resolution for CurrentActor, as the subgraph expects the Gateway to have already translated headers into a serialized user context.

Additionally, to prevent database state bleeding between test suites (Event already exists, duplicate ledger entries), all .e2e-spec.ts files must utilize the @m8a/nestjs-typegoose getModelToken function to aggressively clear their entity collections during the beforeAll hook.