nopSolutions/nopCommerce · error · UnauthorizedAccessException
This is not your product
Error message
This is not your product
What it means
Thrown as UnauthorizedAccessException in PrepareAddSpecificationAttributeModelAsync during the vendor-scoped authorization check. If the current user is a vendor (GetCurrentVendorAsync returns non-null) and the product owning the specification attribute has a different VendorId, the vendor is attempting to access another vendor's product and access is denied. NOTE a latent bug: GetProductByIdAsync(attribute.ProductId) is not null-checked, so a missing product would throw NullReferenceException before this line — but when the product exists and belongs to another vendor, this UnauthorizedAccessException fires.
Source
Thrown at src/Presentation/Nop.Web/Areas/Admin/Factories/ProductModelFactory.cs:1738
AvailableAttributes = await (await _specificationAttributeService.GetSpecificationAttributesWithOptionsAsync())
.SelectAwait(async attributeWithOption =>
{
var attributeName = await GetSpecificationAttributeNameAsync(attributeWithOption);
return new SelectListItem(attributeName, attributeWithOption.Id.ToString());
}).ToListAsync(),
ProductId = productId,
Locales = await _localizedModelFactory.PrepareLocalizedModelsAsync<AddSpecificationAttributeLocalizedModel>()
};
}
var attribute = await _specificationAttributeService.GetProductSpecificationAttributeByIdAsync(specificationId.Value)
?? throw new ArgumentException("No specification attribute found with the specified id");
//a vendor should have access only to his products
var currentVendor = await _workContext.GetCurrentVendorAsync();
if (currentVendor != null && (await _productService.GetProductByIdAsync(attribute.ProductId)).VendorId != currentVendor.Id)
throw new UnauthorizedAccessException("This is not your product");
var specAttributeOption = await _specificationAttributeService.GetSpecificationAttributeOptionByIdAsync(attribute.SpecificationAttributeOptionId);
var specAttribute = await _specificationAttributeService.GetSpecificationAttributeByIdAsync(specAttributeOption.SpecificationAttributeId);
var model = attribute.ToModel<AddSpecificationAttributeModel>();
model.SpecificationId = attribute.Id;
model.AttributeId = specAttribute.Id;
model.AttributeTypeName = await _localizationService.GetLocalizedEnumAsync(attribute.AttributeType);
model.AttributeName = specAttribute.Name;
model.AvailableAttributes = await (await _specificationAttributeService.GetSpecificationAttributesWithOptionsAsync())
.SelectAwait(async attributeWithOption =>
{
var attributeName = await GetSpecificationAttributeNameAsync(attributeWithOption);
return new SelectListItem(attributeName, attributeWithOption.Id.ToString());
})
.ToListAsync();View on GitHub (pinned to 64bdf2ff08)
Solutions
- Confirm the vendor is only editing their own products; restrict the UI so vendors cannot select other vendors' specification attributes.
- Ensure the product's VendorId is correctly assigned to the current vendor before exposing edit links.
- If products were reassigned, re-link or migrate the specification attributes to the correct owner.
- Catch UnauthorizedAccessException and return a clean 403/message instead of letting it propagate.
Example fix
// before
var currentVendor = await _workContext.GetCurrentVendorAsync();
if (currentVendor != null && (await _productService.GetProductByIdAsync(attribute.ProductId)).VendorId != currentVendor.Id)
throw new UnauthorizedAccessException("This is not your product");
// after (null-safe product load + explicit guard)
var currentVendor = await _workContext.GetCurrentVendorAsync();
if (currentVendor != null)
{
var ownerProduct = await _productService.GetProductByIdAsync(attribute.ProductId);
if (ownerProduct is null || ownerProduct.VendorId != currentVendor.Id)
throw new UnauthorizedAccessException("This is not your product");
} Defensive patterns
Strategy: validation
Validate before calling
// Enforce vendor ownership before exposing the editor.
var currentVendor = await _workContext.GetCurrentVendorAsync();
if (currentVendor is not null)
{
var ownerProduct = await _productService.GetProductByIdAsync(attribute.ProductId);
if (ownerProduct is null || ownerProduct.VendorId != currentVendor.Id)
{
_notificationService.ErrorNotification("Access denied: not your product.");
return; // or 403
}
} Type guard
static bool VendorOwnsProduct(Vendor vendor, Product product) => vendor is not null && product is not null && product.VendorId == vendor.Id;
Try / catch
try { /* vendor-scoped load */ }
catch (UnauthorizedAccessException ex) { /* return 403 / forbidden view */ } Prevention
- Scope the vendor UI so only the vendor's own products/specs are listed.
- Null-check the product before reading VendorId (current code can NullReferenceException).
- When reassigning product ownership, migrate dependent specification attributes too.
When it happens
Trigger: A logged-in vendor user requests to edit a product specification attribute whose owning product's VendorId differs from the vendor's own Id. Occurs when a vendor manipulates the request id (e.g., changes specificationId in the URL) to reach another vendor's attribute, or when a product was reassigned to a different vendor after the attribute link was created.
Common situations: Vendor tries to edit another vendor's product spec by altering the id; product ownership transferred but old attribute links still presented; multi-vendor misconfiguration where VendorId on the product is null/0 and mismatches; testing with a vendor account against global-admin products.
Related errors
- Manufacturer not found
- Admin.Catalog.Products.ExceededMaximumNumber
- You cannot delete this email account. At least one account i
- Template cannot be loaded
- Shopping was not initiated by customer
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/03d05adb84a9a835.
Report an issue: GitHub.