Skip to main content

DPP Access Control

The EU ESPR regulation mandates differentiated access to Digital Product Passport data based on user roles and data sensitivity. This guide covers tiered access control, field visibility, and redaction strategies.

Overviewโ€‹

Why Access Control?โ€‹

Digital Product Passports contain sensitive information that must be accessible to different stakeholders at different levels:

  • Consumers need basic environmental impact and composition information
  • Supply chain partners require material sourcing and regulatory certificates
  • Market surveillance authorities need full data access for compliance verification
  • Economic operators (manufacturers/importers) retain control over their data

The ESPR requires granular access control to:

  • Prevent unauthorized disclosure of proprietary formulations
  • Enable consumer transparency
  • Support regulatory oversight
  • Maintain competitive confidentiality

Access Tiersโ€‹

DPPAccessLevel Enumerationโ€‹

enum DPPAccessLevel {
PUBLIC = "public", // Consumers and general public
AUTHENTICATED = "authenticated", // Verified supply chain partners
REGULATORY = "regulatory", // Market surveillance, customs authorities
OWNER = "owner" // Economic operator/manufacturer
}

Tier Characteristicsโ€‹

TierAudiencePurposeTypical Fields Visible
PUBLICConsumers, retailers, general publicProduct transparency, sustainability claimsproductName, carbonFootprint, recycledContent, repairabilityScore, fiberComposition (textiles)
AUTHENTICATEDAuthorized distributors, traders, supply chain partnersSupply chain collaboration, compliance verificationAll PUBLIC + manufacturer details, material sources, factory locations, regulatory certifications
REGULATORYEU enforcement authorities, market surveillance, customsCompliance enforcement, product safety verificationAll AUTHENTICATED + costOfSubstances, dueDiligenceReports, testing protocols, sensitive SVHC concentrations
OWNEREconomic operator, manufacturer, importerFull control and managementAll fields, including proprietary manufacturing processes and internal notes

Default Field Visibilityโ€‹

The following table defines which fields are visible at each access level by default:

FieldData TypePUBLICAUTHREGOWNER
productIdstringโœ“โœ“โœ“โœ“
productNamestringโœ“โœ“โœ“โœ“
manufacturerstringโœ“โœ“โœ“โœ“
countryOfOriginstringโœ“โœ“โœ“โœ“
categoryenumโœ“โœ“โœ“โœ“
carbonFootprintnumberโœ“โœ“โœ“โœ“
recycledContentnumberโœ“โœ“โœ“โœ“
durabilityYearsnumberโœ“โœ“โœ“โœ“
repairabilityScorenumberโœ“โœ“โœ“โœ“
substancesOfConcernarrayโœ“*โœ“*โœ“โœ“
conformityDeclarationsarrayโœ—โœ“โœ“โœ“
sectorDataobjectโœ“*โœ“*โœ“โœ“
manufacturingDetailsobjectโœ—โœ“โœ“โœ“
supplierInformationobjectโœ—โœ“โœ“โœ“
testingReportsarrayโœ—โœ—โœ“โœ“
proprietaryNotesstringโœ—โœ—โœ—โœ“

*Category-specific visibility rules apply (see next section)

Category-Specific Overridesโ€‹

Battery Passport (Battery Regulation Art. 13)โ€‹

Battery substances and sector data must be PUBLIC to enable consumer awareness:

const batteryOverrides = {
substancesOfConcern: {
minimumLevel: DPPAccessLevel.PUBLIC
},
sectorData: {
fields: ["chemistry", "capacityKwh", "cobaltContent", "lithiumContent"],
minimumLevel: DPPAccessLevel.PUBLIC
}
};

Textile Passport (ESPR Annex VI)โ€‹

Fiber composition must be PUBLIC; water usage visible at AUTHENTICATED:

const textileOverrides = {
sectorData: {
fiberComposition: DPPAccessLevel.PUBLIC,
waterUsageLiters: DPPAccessLevel.AUTHENTICATED,
processes: DPPAccessLevel.AUTHENTICATED,
microfiberRelease: DPPAccessLevel.AUTHENTICATED
}
};

Electronics (ESPR Annex VII)โ€‹

Energy consumption and hazardous substances are PUBLIC:

const electronicsOverrides = {
substancesOfConcern: {
minimumLevel: DPPAccessLevel.PUBLIC
},
sectorData: {
energyConsumption: DPPAccessLevel.PUBLIC,
hazardousSubstances: DPPAccessLevel.PUBLIC
}
};

Core Functionsโ€‹

filterDPPByAccess / filter_dpp_by_accessโ€‹

Filters a DPP object to contain only fields visible at a given access level:

import {
DPPAccessLevel,
filterDPPByAccess,
DPPMetadata
} from '@heimdall/dpp-access';

const fullDPP: DPPMetadata = {
productId: "08714043123452",
productName: "Eco Battery Pack 5000mAh",
manufacturer: "Green Energy Corp",
countryOfOrigin: "DE",
category: "battery",
carbonFootprint: 2.5,
recycledContent: 35,
durabilityYears: 5,
repairabilityScore: 7,
substancesOfConcern: [
{
substanceId: "7440-38-2",
name: "Cobalt",
concentration: 8.5,
status: "SVHC"
}
],
conformityDeclarations: [
{
regulation: "Directive 2014/30/EU",
conformityMarked: true,
testingDate: "2025-11-15"
}
],
manufacturingDetails: {
factoryLocation: "Shanghai Plant 2",
processType: "wet chemistry"
},
sectorData: {
chemistry: "lithium-ion",
capacityKwh: 0.018,
cobaltContent: 8.5,
lithiumContent: 4.2
}
};

// PUBLIC view: Only basic transparency data
const publicDPP = filterDPPByAccess(fullDPP, DPPAccessLevel.PUBLIC);
console.log(publicDPP);
/* Output:
{
productId: "08714043123452",
productName: "Eco Battery Pack 5000mAh",
manufacturer: "Green Energy Corp",
carbonFootprint: 2.5,
recycledContent: 35,
repairabilityScore: 7,
substancesOfConcern: [...], // SVHC data visible per Battery Reg
sectorData: {
chemistry: "lithium-ion",
capacityKwh: 0.018,
cobaltContent: 8.5,
lithiumContent: 4.2
}
// conformityDeclarations, manufacturingDetails NOT included
}
*/

// AUTHENTICATED view: Supply chain partner data
const authDPP = filterDPPByAccess(fullDPP, DPPAccessLevel.AUTHENTICATED);
console.log(authDPP);
/* Includes:
...publicDPP,
manufacturingDetails,
conformityDeclarations
// testingReports NOT included
*/

// REGULATORY view: Full enforcement authority access
const regDPP = filterDPPByAccess(fullDPP, DPPAccessLevel.REGULATORY);
console.log(regDPP);
// Includes all fields except proprietaryNotes

// OWNER view: Manufacturer retains all data
const ownerDPP = filterDPPByAccess(fullDPP, DPPAccessLevel.OWNER);
console.log(ownerDPP === fullDPP); // true (no redaction)

Redaction Modesโ€‹

Control how restricted fields are handled:

import { RedactionMode } from '@heimdall/dpp-access';

// Mode 1: OMIT (default)
// Restricted fields are completely removed from output
const filtered1 = filterDPPByAccess(fullDPP, DPPAccessLevel.PUBLIC, {
redactionMode: RedactionMode.OMIT
});
// conformityDeclarations field does not exist in output

// Mode 2: PLACEHOLDER
// Restricted fields are included but marked as redacted
const filtered2 = filterDPPByAccess(fullDPP, DPPAccessLevel.PUBLIC, {
redactionMode: RedactionMode.PLACEHOLDER
});
console.log(filtered2.conformityDeclarations);
// Output: "[REDACTED]"

// Mode 3: SHADOW
// Array fields show count; object fields show keys only
const filtered3 = filterDPPByAccess(fullDPP, DPPAccessLevel.PUBLIC, {
redactionMode: RedactionMode.SHADOW
});
console.log(filtered3.conformityDeclarations);
// Output: { count: 1, summary: "1 declaration(s) restricted" }

Authorization Checksโ€‹

isAtLeast / is_at_leastโ€‹

Verify user authorization before returning DPP data:

import { DPPAccessLevel } from '@heimdall/dpp-access';

class DPPService {
getDPP(productId: string, userAccessLevel: DPPAccessLevel): DPPMetadata {
const fullDPP = this.repository.findById(productId);

// Check authorization
if (!userAccessLevel.isAtLeast(DPPAccessLevel.PUBLIC)) {
throw new UnauthorizedError("User not permitted to view DPP");
}

// Filter and return
return filterDPPByAccess(fullDPP, userAccessLevel);
}

/**
* Example authorization checks in REST endpoint
*/
async getPublicDPP(req: Request, res: Response) {
const userLevel = DPPAccessLevel.PUBLIC;

const dpp = this.getDPP(req.params.productId, userLevel);
res.json(dpp);
}

async getAuthenticatedDPP(req: Request, res: Response) {
// Verify OAuth token or API key
const userLevel = await this.authService.verifyToken(req.headers.authorization);

if (!userLevel.isAtLeast(DPPAccessLevel.AUTHENTICATED)) {
return res.status(403).json({ error: "Insufficient permissions" });
}

const dpp = this.getDPP(req.params.productId, userLevel);
res.json(dpp);
}

async getRegulatoryDPP(req: Request, res: Response) {
// Verify regulatory authority certificate
const cert = req.client.cert;
const userLevel = this.authService.verifyRegulatoryAuthority(cert);

if (!userLevel.isAtLeast(DPPAccessLevel.REGULATORY)) {
return res.status(403).json({ error: "Not an authorized authority" });
}

const dpp = this.getDPP(req.params.productId, userLevel);
res.json(dpp);
}
}

Field-Level Visibility Customizationโ€‹

Override default visibility for specific fields:

import { filterDPPByAccess, DPPAccessLevel } from '@heimdall/dpp-access';

const customConfig = {
fieldOverrides: {
// Make factory location visible to AUTHENTICATED users
"manufacturingDetails.factoryLocation": DPPAccessLevel.AUTHENTICATED,

// Restrict recycled content percentage to REGULATORY only
"recycledContent": DPPAccessLevel.REGULATORY,

// Hide proprietary notes from all but OWNER
"proprietaryNotes": DPPAccessLevel.OWNER,

// Make certification date public (override default)
"conformityDeclarations.testingDate": DPPAccessLevel.PUBLIC
}
};

const publicDPP = filterDPPByAccess(fullDPP, DPPAccessLevel.PUBLIC, customConfig);
// recycledContent is now hidden from public view

Compliance Audit Trailโ€‹

Log access requests to meet ESPR audit requirements:

interface AccessLog {
timestamp: string;
productId: string;
requestingParty: string;
accessLevel: DPPAccessLevel;
fieldsRetrieved: string[];
dataHash: string; // SHA-256 of returned data
reason: string; // "CONSUMER_VIEW" | "SUPPLY_CHAIN" | "INSPECTION" | etc.
}

class AuditedDPPService extends DPPService {
async getDPP(productId: string, userAccessLevel: DPPAccessLevel): Promise<DPPMetadata> {
const fullDPP = await super.getDPP(productId, userAccessLevel);

// Log access
await this.auditLog.record({
timestamp: new Date().toISOString(),
productId,
requestingParty: this.context.userId,
accessLevel: userAccessLevel,
fieldsRetrieved: Object.keys(fullDPP),
dataHash: sha256(JSON.stringify(fullDPP)),
reason: this.context.accessReason
});

return fullDPP;
}
}

Best Practicesโ€‹

1. Default to Least Privilegeโ€‹

Always start with the most restrictive access level and escalate only upon verification.

2. Regular Access Reviewsโ€‹

Audit access logs monthly to identify unusual patterns or unauthorized access attempts.

3. Separate Public and Private URLsโ€‹

Provide separate endpoints for each access tier:

  • /dpp/{id} โ€” PUBLIC
  • /api/dpp/{id} (OAuth required) โ€” AUTHENTICATED
  • /admin/dpp/{id} (cert + role required) โ€” REGULATORY

4. Document Overridesโ€‹

Any field-level visibility overrides should be documented with regulatory justification.

5. Monitor Category-Specific Rulesโ€‹

Battery, textile, and electronics DPPs have distinct public visibility requirements. Ensure compliance in your access control configuration.

6. Implement Gradual Degradationโ€‹

When access is denied, provide helpful messaging:

"This field is not visible at your access level (PUBLIC).
Authenticate as a supply chain partner to view manufacturing details."