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

  1. Confirm the vendor is only editing their own products; restrict the UI so vendors cannot select other vendors' specification attributes.
  2. Ensure the product's VendorId is correctly assigned to the current vendor before exposing edit links.
  3. If products were reassigned, re-link or migrate the specification attributes to the correct owner.
  4. 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

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


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/03d05adb84a9a835. Report an issue: GitHub.