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
- Gateway Parsing: The Gateway validates incoming JWTs and extracts the
x-user-idheader. - CurrentActor Decorator: The
@CurrentActor()decorator decodes theuserpayload injected by the gateway or extractsx-user-idto identify the requester. - Guard Enforcement: The custom
PermissionsGuard(implemented via@app/commonand dynamically enforced) intercepts the GraphQL context. - Permission Resolution: The guard extracts the
x-org-idand 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.
Related
- Experiences Subgraph โ Read the high-level subgraph overview
- Custom RBAC Policies โ Learn how custom roles are resolved
- Testing Architecture โ Review the full E2E setup standard