Configuring visibility and purchase authorization separately per customer group
Catalog Permissions makes it possible to lock or unlock categories and products for individual customer groups, separating pure visibility from actual purchase authorization. This article explains the mechanics behind the feature, a typical B2B scenario with an unlocked product line, and the noticeable impact on full page cache, indexing and category page performance.
Table of Contents
- 1. Scope: visibility and purchase authorization as separate axes
- 2. Configuration at the category level
- 3. The Catalog Permissions indexer and its data model
- 4. How Catalog Permissions affects the full page cache
- 5. Impact on layered navigation and facets
- 6. GraphQL and storefront API behavior under Catalog Permissions
- 7. B2B example: an unlocked product line
- 8. How this differs from, and combines with, Shared Catalogs in the B2B module
- 9. Pitfalls and monitoring in production
- 10. Summary
- 11. FAQ
1. Scope: visibility and purchase authorization as separate axes
Catalog Permissions is an Adobe Commerce feature and, like the Recommendations module, is not available in Magento Open Source. Unlike simple customer group pricing rules, it exposes three independent permission axes per category: catalog visibility, price display, and authorization to actually add a product to the cart.
That separation is the key difference from plain product visibility through the standard visibility attribute, which only distinguishes binary between visible and invisible, without any per customer group distinction. With Catalog Permissions, a category can be entirely invisible to guests, visible without pricing to registered customers, and fully purchasable for an unlocked B2B group.
Each of the three axes supports the values allow, deny and inherit from the parent category, which lets permissions cascade hierarchically without configuring every subcategory individually.
2. Configuration at the category level
Configuration happens in the category editor under the Permissions tab, where the three permissions get set per customer group and optionally per website. Without an explicit rule, a category defaults to visible and purchasable for every group, so the feature has to be actively used to add restrictions, not the other way around.
For the B2B scenario in this article, a dedicated customer group for approved trade customers gets created, and the relevant product line category gets a deny rule for visibility for the standard groups NOT LOGGED IN and GENERAL, while the new trade customer group gets allow across all three axes.
<!-- Excerpt from a setup data patch class that sets Catalog Permissions rules programmatically -->
<?php
declare(strict_types=1);
namespace Mironsoft\B2bCatalog\Setup\Patch\Data;
use Magento\CatalogPermissions\App\ConfigInterface as PermissionsConfig;
use Magento\CatalogPermissions\Model\Permission;
use Magento\Framework\Setup\Patch\DataPatchInterface;
/**
* Sets Catalog Permissions rules for the trade customer product line programmatically.
*/
class RestrictWholesaleCategory implements DataPatchInterface
{
public function apply(): void
{
// Category and customer group IDs come from earlier patches in this module.
}
public static function getDependencies(): array
{
return [];
}
public function getAliases(): array
{
return [];
}
}
3. The Catalog Permissions indexer and its data model
Catalog Permissions ships its own indexer that materializes the permission matrix built from category, customer group and website into the catalogpermissions_index table. Only that index makes it possible to check, on every category or product request, whether the current customer group has access, without re resolving the inheritance hierarchy on every single request.
The indexer defaults to update on save mode but can reasonably switch to schedule mode on larger catalogs, especially when permission changes typically happen in batches, such as unlocking a group of new trade customers once a month. Every change to the permission matrix, whether through the category editor or programmatically, has to trigger the indexer, or stale permissions remain in effect on the storefront.
4. How Catalog Permissions affects the full page cache
To keep the full page cache from accidentally serving one customer group's content to another, Catalog Permissions registers an additional vary value in the HTTP context that folds customer group membership into the cache key. That produces as many cache variants for the same category page as there are distinct permission combinations, not just one per store.
On a store with ten customer groups and Catalog Permissions active, a single category page can theoretically need up to ten times as many cache entries, which noticeably lowers the effective cache hit rate, particularly for rarely visited combinations of category and customer group that get recomputed more often as cache misses.
In practice it is therefore worth applying Catalog Permissions as narrowly as possible, only to the categories that actually need restriction, rather than turning it on store wide for the entire catalog, to keep the number of cache variants manageable.
5. Impact on layered navigation and facets
Layered navigation has to honor Catalog Permissions rules as well, so restricted products do not become indirectly visible through facets such as brand or price, even though the associated category is actually hidden for the current customer group. The search index filters using the same catalogpermissions_index data before facet aggregations get computed.
In practice that means facet counts can differ between customer groups even on an identical category page, something worth keeping in mind while debugging: a seemingly wrong facet count is often not a bug at all, but correct behavior for a different, logged in customer group.
6. GraphQL and storefront API behavior under Catalog Permissions
A restricted category does not return an error object through the GraphQL API, it simply returns no products, or an empty category response, depending on the bearer token or guest context of the request. The frontend has to catch that case explicitly and should not treat it like a genuine server error, but instead show a regular, plausible empty category page for that particular user.
A similar logic applies to purchase authorization in the checkout mutation resolver: attempting to add a product without purchase authorization to the cart returns an explicit error code from the addProductsToCart mutation, which the frontend should treat as a permission error rather than a generic out of stock error.
{
"errors": [
{
"message": "The requested product cannot be added to the cart for this customer group.",
"extensions": {
"category": "graphql-input-error",
"sku": "B2B-WHOLESALE-100"
}
}
]
}
7. B2B example: an unlocked product line
A typical scenario: a manufacturer sells a separate trade customer product line alongside its consumer assortment, with different terms, that should stay invisible to both guests and regular registered customers. The category for that product line gets a deny visibility rule for the standard groups, while a newly created trade customer group, to which customers only get assigned after manual review in the admin, gets full access.
On top of that, the customer group assignment inside the customer account editor can be used to make sure a customer only moves into the trade customer group after a sales representative approves them, effectively tying the product line's visibility to a manual approval process without requiring any custom development.
8. How this differs from, and combines with, Shared Catalogs in the B2B module
Adobe Commerce B2B ships with Shared Catalogs, a related but distinct concept that frequently gets confused with Catalog Permissions. A Shared Catalog mainly controls individual price lists and assortment subsets per company account, while Catalog Permissions is a plain yes or no visibility and purchase authorization check per customer group, independent of company specific pricing.
In practice, the two mechanisms combine well: Catalog Permissions hides an entire product line from everyone except the unlocked customer group, while within that group, different Shared Catalogs can additionally define different prices and assortment subsets per company account. Anyone who only needs company specific pricing without a real visibility gate usually gets by with Shared Catalogs alone and can skip Catalog Permissions entirely.
For the architecture decision, it matters that Catalog Permissions operates at the category level and therefore also affects navigation and search, while Shared Catalogs operate at the product and price level within an already visible catalog. Anyone enabling both features at once should carefully test their interaction, particularly around indexing, since both mechanisms ship their own indexer with its own schedule.
9. Pitfalls and monitoring in production
A commonly overlooked pitfall is a forgotten reindex after a bulk change to customer group assignments, for example after a CSV import of new B2B customers. Until the catalogpermissions_index runs, newly assigned customers still see the old, usually more restrictive permissions, which in practice leads to support tickets before the indexer runs manually or on schedule.
It is also worth factoring the additional cache overhead into full page cache capacity planning early on, since active Catalog Permissions requires noticeably more cache variants to be held than a standard setup without customer group specific visibility.
| Permission axis | Values | Effect | Touches indexer |
|---|---|---|---|
| Visibility (view) | Allow, Deny, Inherit | Category appears in navigation and search or not | Yes, catalog_permissions |
| Price display (price) | Allow, Deny, Inherit | Price shown or replaced with a login prompt | Yes, catalog_permissions |
| Checkout authorization | Allow, Deny, Inherit | Product can be added to cart or not | Yes, catalog_permissions |
| Inheritance | From parent category | Subcategories inherit the parent category's rule | Indirectly via category tree |
| Website scope | Optional per website | Allows different rules per website within the same store | Yes, per website ID |
Mironsoft
Magento development, module consulting, and system architecture
A Magento project that needs a second opinion or experienced execution?
We build custom Magento modules, advise on architecture decisions, and take on complex implementations, from service contract planning to production-ready deployment.
Architecture Consulting
Have module and system architecture thought through properly before you build.
Custom Module Development
Build custom Magento modules cleanly, following best practices.
Code Review & Audit
Have existing modules reviewed for performance, security, and maintainability.
10. Summary
Catalog Permissions
Concept
Three separate axes, visibility, price display and checkout authorization, each configurable per customer group and category.
Indexer
The catalogpermissions_index table materializes the permission matrix, every change requires a reindex.
Performance
The full page cache gains a per customer group vary value, noticeably increasing the number of cache variants.
B2B use
Unlocked product lines can be modeled through a dedicated customer group without any custom code.