Data Privacy in API Validation: GDPR and CCPA Compliance
Navigate data privacy regulations in API validation with comprehensive GDPR and CCPA compliance strategies.
Table of Contents
Table of Contents
Data Privacy in API Validation: GDPR and CCPA Compliance
Data privacy regulations significantly impact API validation and data processing. Understanding GDPR, CCPA, and other privacy laws is essential for compliant API development and operation.
Data Privacy in API Validation Overview
Security Threat Landscape
Data Privacy in API Validation requires careful consideration of multiple factors that impact implementation success and user experience.
Key Considerations
Technical Requirements
- Scalable architecture design
- Performance optimization strategies
- Error handling and recovery
- Security and compliance measures
Business Impact
- User experience enhancement
- Operational efficiency gains
- Cost optimization opportunities
- Risk mitigation strategies
Business Impact
- User experience enhancement
- Operational efficiency gains
- Cost optimization opportunities
- Risk mitigation strategies
Practical Implementation Examples
GDPR Compliance Engine for API Validation
// Production-ready GDPR compliance engine for API data validation
interface GDPRComplianceConfig {
dataSubjectRights: {
access: boolean
rectification: boolean
erasure: boolean
restriction: boolean
portability: boolean
objection: boolean
}
dataProcessing: {
consentRequired: boolean
legitimateInterest: boolean
contract: boolean
legalObligation: boolean
vitalInterests: boolean
publicTask: boolean
}
dataRetention: {
defaultPeriod: number // days
maxPeriod: number // days
automaticDeletion: boolean
}
consentManagement: {
explicitConsent: boolean
withdrawalMechanism: boolean
consentAudit: boolean
}
dataSecurity: {
encryption: boolean
pseudonymization: boolean
accessLogging: boolean
}
}
interface GDPRDataProcessingRecord {
id: string
dataSubjectId: string
processingPurpose: string
legalBasis: 'consent' | 'contract' | 'legal_obligation' | 'vital_interests' | 'public_task' | 'legitimate_interest'
dataCategories: string[]
retentionPeriod: number
consentId?: string
consentTimestamp?: number
withdrawalTimestamp?: number
processingStartDate: number
processingEndDate?: number
lastAccessDate: number
accessCount: number
dataController: string
dataProcessor?: string
thirdCountryTransfer: boolean
safeguards?: string[]
}
interface GDPRComplianceResult {
isCompliant: boolean
violations: string[]
warnings: string[]
requiredActions: string[]
legalBases: string[]
retentionInfo: {
canProcess: boolean
retentionPeriod: number
deletionDate: number
reason: string
}
consentInfo: {
hasValidConsent: boolean
consentId?: string
consentDate?: number
withdrawalDate?: number
}
}
class GDPRComplianceEngine {
private config: GDPRComplianceConfig
private processingRecords: Map<string, GDPRDataProcessingRecord> = new Map()
private consentRecords: Map<string, any> = new Map()
private auditLog: Array<{
timestamp: number
action: string
dataSubjectId: string
details: any
}> = new Map()
constructor(config: GDPRComplianceConfig) {
this.config = config
this.initializeComplianceEngine()
}
async validateDataProcessing(
dataSubjectId: string,
processingPurpose: string,
dataCategories: string[],
legalBasis: GDPRDataProcessingRecord['legalBasis']
): Promise<GDPRComplianceResult> {
const violations: string[] = []
const warnings: string[] = []
const requiredActions: string[] = []
const legalBases: string[] = []
try {
// Check if legal basis is valid for the processing purpose
const legalBasisValid = this.validateLegalBasis(processingPurpose, legalBasis)
if (!legalBasisValid.isValid) {
violations.push(`Invalid legal basis: ${legalBasisValid.reason}`)
} else {
legalBases.push(legalBasis)
}
// Check consent requirements
if (this.requiresConsent(processingPurpose)) {
const consentCheck = await this.validateConsent(dataSubjectId, processingPurpose)
if (!consentCheck.isValid) {
violations.push(`Consent required: ${consentCheck.reason}`)
requiredActions.push('Obtain valid consent from data subject')
}
}
// Check data retention compliance
const retentionCheck = this.validateRetentionPolicy(dataSubjectId, processingPurpose)
if (!retentionCheck.isCompliant) {
violations.push(`Retention policy violation: ${retentionCheck.reason}`)
}
// Check data minimization
const minimizationCheck = this.validateDataMinimization(dataCategories, processingPurpose)
if (!minimizationCheck.isCompliant) {
warnings.push(`Data minimization concern: ${minimizationCheck.reason}`)
}
// Check purpose limitation
const purposeCheck = this.validatePurposeLimitation(dataSubjectId, processingPurpose)
if (!purposeCheck.isCompliant) {
violations.push(`Purpose limitation violation: ${purposeCheck.reason}`)
}
// Validate data security measures
const securityCheck = this.validateDataSecurity(dataCategories)
if (!securityCheck.isCompliant) {
requiredActions.push(`Implement required security measures: ${securityCheck.missingMeasures.join(', ')}`)
}
// Check third country transfers
if (this.involvesThirdCountryTransfer(dataCategories)) {
const transferCheck = this.validateInternationalTransfers(dataSubjectId, dataCategories)
if (!transferCheck.isCompliant) {
violations.push(`International transfer violation: ${transferCheck.reason}`)
}
}
// Generate compliance record
const recordId = this.generateRecordId()
const processingRecord: GDPRDataProcessingRecord = {
id: recordId,
dataSubjectId,
processingPurpose,
legalBasis,
dataCategories,
retentionPeriod: this.calculateRetentionPeriod(processingPurpose),
consentId: legalBasis === 'consent' ? this.generateConsentId() : undefined,
consentTimestamp: legalBasis === 'consent' ? Date.now() : undefined,
processingStartDate: Date.now(),
lastAccessDate: Date.now(),
accessCount: 1,
dataController: this.config.dataController || 'Default Controller',
thirdCountryTransfer: this.involvesThirdCountryTransfer(dataCategories)
}
this.processingRecords.set(recordId, processingRecord)
this.logAuditEvent('data_processing_validated', dataSubjectId, { recordId, processingPurpose })
return {
isCompliant: violations.length === 0,
violations,
warnings,
requiredActions,
legalBases,
retentionInfo: retentionCheck,
consentInfo: await this.getConsentInfo(dataSubjectId, processingPurpose)
}
} catch (error) {
console.error('GDPR compliance validation failed:', error)
return {
isCompliant: false,
violations: ['Validation system error'],
warnings: [],
requiredActions: ['Contact system administrator'],
legalBases: [],
retentionInfo: {
canProcess: false,
retentionPeriod: 0,
deletionDate: Date.now(),
reason: 'System error'
},
consentInfo: {
hasValidConsent: false
}
}
}
}
private validateLegalBasis(
purpose: string,
legalBasis: GDPRDataProcessingRecord['legalBasis']
): { isValid: boolean; reason?: string } {
// Define valid legal bases for different purposes
const validBases: Record<string, string[]> = {
'marketing': ['consent', 'legitimate_interest'],
'analytics': ['consent', 'legitimate_interest'],
'customer_service': ['contract', 'consent'],
'fraud_prevention': ['legitimate_interest', 'legal_obligation'],
'legal_compliance': ['legal_obligation'],
'vital_interests': ['vital_interests'],
'public_task': ['public_task']
}
const purposeKey = Object.keys(validBases).find(key =>
purpose.toLowerCase().includes(key.replace('_', ' '))
) || 'other'
if (purposeKey === 'other') {
return { isValid: false, reason: 'Unrecognized processing purpose' }
}
if (!validBases[purposeKey].includes(legalBasis)) {
return {
isValid: false,
reason: `Legal basis '${legalBasis}' not valid for purpose '${purpose}'`
}
}
return { isValid: true }
}
private requiresConsent(purpose: string): boolean {
const consentRequiredPurposes = [
'marketing',
'analytics',
'profiling'
]
return consentRequiredPurposes.some(p => purpose.toLowerCase().includes(p))
}
private async validateConsent(dataSubjectId: string, purpose: string): Promise<{ isValid: boolean; reason?: string }> {
const consentRecord = this.consentRecords.get(`${dataSubjectId}-${purpose}`)
if (!consentRecord) {
return { isValid: false, reason: 'No consent record found' }
}
if (consentRecord.withdrawn) {
return { isValid: false, reason: 'Consent has been withdrawn' }
}
if (Date.now() - consentRecord.timestamp > 365 * 24 * 60 * 60 * 1000) {
return { isValid: false, reason: 'Consent has expired' }
}
return { isValid: true }
}
private validateRetentionPolicy(dataSubjectId: string, purpose: string): {
isCompliant: boolean
retentionPeriod: number
deletionDate: number
reason: string
} {
const record = Array.from(this.processingRecords.values())
.find(r => r.dataSubjectId === dataSubjectId && r.processingPurpose === purpose)
if (!record) {
return {
isCompliant: false,
retentionPeriod: 0,
deletionDate: Date.now(),
reason: 'No processing record found'
}
}
const deletionDate = record.processingStartDate + (record.retentionPeriod * 24 * 60 * 60 * 1000)
if (Date.now() > deletionDate) {
return {
isCompliant: false,
retentionPeriod: record.retentionPeriod,
deletionDate,
reason: 'Data retention period exceeded'
}
}
return {
isCompliant: true,
retentionPeriod: record.retentionPeriod,
deletionDate,
reason: 'Within retention period'
}
}
private validateDataMinimization(dataCategories: string[], purpose: string): {
isCompliant: boolean
reason?: string
} {
// Define minimum required data for different purposes
const requiredData: Record<string, string[]> = {
'authentication': ['email', 'password_hash'],
'payment': ['payment_method', 'billing_address'],
'shipping': ['shipping_address', 'contact_info']
}
const purposeKey = Object.keys(requiredData).find(key =>
purpose.toLowerCase().includes(key)
)
if (purposeKey) {
const required = requiredData[purposeKey]
const hasAllRequired = required.every(cat => dataCategories.includes(cat))
if (!hasAllRequired) {
return {
isCompliant: false,
reason: `Missing required data categories: ${required.filter(cat => !dataCategories.includes(cat)).join(', ')}`
}
}
}
return { isCompliant: true }
}
private validatePurposeLimitation(dataSubjectId: string, purpose: string): {
isCompliant: boolean
reason?: string
} {
const subjectRecords = Array.from(this.processingRecords.values())
.filter(r => r.dataSubjectId === dataSubjectId)
// Check if this purpose is compatible with existing purposes
const existingPurposes = subjectRecords.map(r => r.processingPurpose)
const compatiblePurposes = this.getCompatiblePurposes(purpose)
const hasIncompatiblePurpose = existingPurposes.some(existing =>
!compatiblePurposes.includes(existing)
)
if (hasIncompatiblePurpose) {
return {
isCompliant: false,
reason: 'Purpose not compatible with existing data processing'
}
}
return { isCompliant: true }
}
private validateDataSecurity(dataCategories: string[]): {
isCompliant: boolean
missingMeasures: string[]
} {
const missingMeasures: string[] = []
const sensitiveCategories = ['personal_data', 'financial_data', 'health_data']
if (dataCategories.some(cat => sensitiveCategories.includes(cat))) {
if (!this.config.dataSecurity.encryption) {
missingMeasures.push('encryption')
}
if (!this.config.dataSecurity.pseudonymization) {
missingMeasures.push('pseudonymization')
}
}
return {
isCompliant: missingMeasures.length === 0,
missingMeasures
}
}
private validateInternationalTransfers(dataSubjectId: string, dataCategories: string[]): {
isCompliant: boolean
reason?: string
} {
// In production, check against adequacy decisions, BCRs, SCCs, etc.
// For demo, simulate transfer validation
if (this.config.thirdCountryTransfer) {
return {
isCompliant: false,
reason: 'Third country transfer requires additional safeguards'
}
}
return { isCompliant: true }
}
private calculateRetentionPeriod(purpose: string): number {
// Define retention periods for different purposes (in days)
const retentionPeriods: Record<string, number> = {
'marketing': 1095, // 3 years
'analytics': 2555, // 7 years
'customer_service': 365, // 1 year
'fraud_prevention': 2555, // 7 years
'legal_compliance': 2555, // 7 years
'default': 365 // 1 year
}
const purposeKey = Object.keys(retentionPeriods).find(key =>
purpose.toLowerCase().includes(key.replace('_', ' '))
) || 'default'
return retentionPeriods[purposeKey]
}
private async getConsentInfo(dataSubjectId: string, purpose: string): Promise<{
hasValidConsent: boolean
consentId?: string
consentDate?: number
withdrawalDate?: number
}> {
const consentRecord = this.consentRecords.get(`${dataSubjectId}-${purpose}`)
if (!consentRecord) {
return { hasValidConsent: false }
}
return {
hasValidConsent: !consentRecord.withdrawn && (Date.now() - consentRecord.timestamp) <= 365 * 24 * 60 * 60 * 1000,
consentId: consentRecord.id,
consentDate: consentRecord.timestamp,
withdrawalDate: consentRecord.withdrawalDate
}
}
private getCompatiblePurposes(purpose: string): string[] {
// Define which purposes are compatible with each other
const compatibilityMap: Record<string, string[]> = {
'marketing': ['analytics', 'customer_service'],
'analytics': ['marketing', 'fraud_prevention'],
'customer_service': ['marketing', 'fraud_prevention'],
'fraud_prevention': ['analytics', 'customer_service', 'legal_compliance'],
'legal_compliance': ['fraud_prevention']
}
return compatibilityMap[purpose] || []
}
private involvesThirdCountryTransfer(dataCategories: string[]): boolean {
// In production, check if data will be transferred outside EU/EEA
// For demo, simulate based on data categories
const sensitiveCategories = ['personal_data', 'financial_data']
return dataCategories.some(cat => sensitiveCategories.includes(cat))
}
private generateRecordId(): string {
return `gdpr_record_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}
private generateConsentId(): string {
return `consent_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}
private logAuditEvent(action: string, dataSubjectId: string, details: any): void {
this.auditLog.push({
timestamp: Date.now(),
action,
dataSubjectId,
details
})
// Keep only last 10000 audit entries
if (this.auditLog.length > 10000) {
this.auditLog.shift()
}
}
private initializeComplianceEngine(): void {
console.log('GDPR Compliance Engine initialized')
}
// Data Subject Rights Implementation
async handleDataSubjectRequest(
dataSubjectId: string,
right: keyof GDPRComplianceConfig['dataSubjectRights'],
requestDetails?: any
): Promise<{
success: boolean
message: string
data?: any
deletionDate?: number
}> {
switch (right) {
case 'access':
return this.handleAccessRequest(dataSubjectId)
case 'rectification':
return this.handleRectificationRequest(dataSubjectId, requestDetails)
case 'erasure':
return this.handleErasureRequest(dataSubjectId)
case 'restriction':
return this.handleRestrictionRequest(dataSubjectId)
case 'portability':
return this.handlePortabilityRequest(dataSubjectId)
case 'objection':
return this.handleObjectionRequest(dataSubjectId, requestDetails)
default:
return {
success: false,
message: 'Unknown right requested'
}
}
}
private async handleAccessRequest(dataSubjectId: string): Promise<any> {
const subjectRecords = Array.from(this.processingRecords.values())
.filter(r => r.dataSubjectId === dataSubjectId)
this.logAuditEvent('access_request', dataSubjectId, { recordCount: subjectRecords.length })
return {
success: true,
message: 'Access request processed',
data: {
records: subjectRecords.length,
purposes: subjectRecords.map(r => r.processingPurpose),
retentionInfo: subjectRecords.map(r => ({
purpose: r.processingPurpose,
deletionDate: r.processingStartDate + (r.retentionPeriod * 24 * 60 * 60 * 1000)
}))
}
}
}
private async handleRectificationRequest(dataSubjectId: string, corrections: any): Promise<any> {
// In production, update data in underlying systems
this.logAuditEvent('rectification_request', dataSubjectId, { corrections })
return {
success: true,
message: 'Data rectification completed'
}
}
private async handleErasureRequest(dataSubjectId: string): Promise<any> {
const subjectRecords = Array.from(this.processingRecords.values())
.filter(r => r.dataSubjectId === dataSubjectId)
// Mark records for deletion
subjectRecords.forEach(record => {
record.processingEndDate = Date.now()
})
const deletionDate = Date.now() + 30 * 24 * 60 * 60 * 1000 // 30 days
this.logAuditEvent('erasure_request', dataSubjectId, {
recordsMarked: subjectRecords.length,
deletionDate
})
return {
success: true,
message: 'Erasure request processed',
deletionDate
}
}
private async handleRestrictionRequest(dataSubjectId: string): Promise<any> {
// In production, restrict processing of data
this.logAuditEvent('restriction_request', dataSubjectId, {})
return {
success: true,
message: 'Data processing restricted'
}
}
private async handlePortabilityRequest(dataSubjectId: string): Promise<any> {
const subjectRecords = Array.from(this.processingRecords.values())
.filter(r => r.dataSubjectId === dataSubjectId)
const exportData = {
personalData: subjectRecords.map(r => ({
purpose: r.processingPurpose,
categories: r.dataCategories,
collectionDate: r.processingStartDate,
lastAccess: r.lastAccessDate
})),
exportDate: Date.now(),
format: 'JSON'
}
this.logAuditEvent('portability_request', dataSubjectId, { dataSize: JSON.stringify(exportData).length })
return {
success: true,
message: 'Data portability package generated',
data: exportData
}
}
private async handleObjectionRequest(dataSubjectId: string, reason: string): Promise<any> {
// In production, stop specific processing activities
this.logAuditEvent('objection_request', dataSubjectId, { reason })
return {
success: true,
message: 'Objection processed'
}
}
// Consent Management
async recordConsent(
dataSubjectId: string,
purpose: string,
consentGiven: boolean,
consentText?: string
): Promise<string> {
const consentId = this.generateConsentId()
const consentRecord = {
id: consentId,
dataSubjectId,
purpose,
consentGiven,
consentText,
timestamp: Date.now(),
withdrawn: false,
withdrawalDate: null
}
this.consentRecords.set(`${dataSubjectId}-${purpose}`, consentRecord)
this.logAuditEvent('consent_recorded', dataSubjectId, { consentId, purpose, given: consentGiven })
return consentId
}
async withdrawConsent(dataSubjectId: string, purpose: string): Promise<boolean> {
const consentRecord = this.consentRecords.get(`${dataSubjectId}-${purpose}`)
if (!consentRecord) {
return false
}
consentRecord.withdrawn = true
consentRecord.withdrawalDate = Date.now()
this.logAuditEvent('consent_withdrawn', dataSubjectId, { purpose })
return true
}
// Data Retention Management
async processRetentionCleanup(): Promise<{
recordsDeleted: number
recordsScheduled: number
totalRecords: number
}> {
const now = Date.now()
let recordsDeleted = 0
let recordsScheduled = 0
for (const [recordId, record] of this.processingRecords.entries()) {
if (record.processingEndDate) {
// Record marked for deletion
const deletionEligibleDate = record.processingEndDate + 30 * 24 * 60 * 60 * 1000 // 30 days grace period
if (now >= deletionEligibleDate) {
this.processingRecords.delete(recordId)
recordsDeleted++
} else {
recordsScheduled++
}
} else {
// Check if retention period exceeded
const deletionDate = record.processingStartDate + (record.retentionPeriod * 24 * 60 * 60 * 1000)
if (now >= deletionDate) {
record.processingEndDate = now
recordsScheduled++
}
}
}
this.logAuditEvent('retention_cleanup', 'system', {
recordsDeleted,
recordsScheduled,
timestamp: now
})
return {
recordsDeleted,
recordsScheduled,
totalRecords: this.processingRecords.size
}
}
// Compliance Reporting
generateComplianceReport(period: { start: number; end: number }): {
totalRecords: number
compliantRecords: number
violations: number
consentWithdrawals: number
dataSubjectRequests: number
internationalTransfers: number
} {
const periodRecords = Array.from(this.processingRecords.values())
.filter(r => r.processingStartDate >= period.start && r.processingStartDate <= period.end)
const auditEvents = this.auditLog.filter(
event => event.timestamp >= period.start && event.timestamp <= period.end
)
return {
totalRecords: periodRecords.length,
compliantRecords: periodRecords.filter(r => r.processingEndDate === undefined).length,
violations: auditEvents.filter(e => e.action.includes('violation')).length,
consentWithdrawals: auditEvents.filter(e => e.action === 'consent_withdrawn').length,
dataSubjectRequests: auditEvents.filter(e =>
['access_request', 'erasure_request', 'portability_request'].includes(e.action)
).length,
internationalTransfers: periodRecords.filter(r => r.thirdCountryTransfer).length
}
}
}
// Initialize GDPR compliance engine
const gdprConfig: GDPRComplianceConfig = {
dataSubjectRights: {
access: true,
rectification: true,
erasure: true,
restriction: true,
portability: true,
objection: true
},
dataProcessing: {
consentRequired: true,
legitimateInterest: true,
contract: true,
legalObligation: true,
vitalInterests: true,
publicTask: true
},
dataRetention: {
defaultPeriod: 365,
maxPeriod: 2555,
automaticDeletion: true
},
consentManagement: {
explicitConsent: true,
withdrawalMechanism: true,
consentAudit: true
},
dataSecurity: {
encryption: true,
pseudonymization: true,
accessLogging: true
}
}
const gdprComplianceEngine = new GDPRComplianceEngine(gdprConfig)
// GDPR compliance validation endpoint
app.post('/api/gdpr/validate-processing', async (req, res) => {
try {
const { dataSubjectId, processingPurpose, dataCategories, legalBasis } = req.body
if (!dataSubjectId || !processingPurpose || !dataCategories || !legalBasis) {
return res.status(400).json({ error: 'dataSubjectId, processingPurpose, dataCategories, and legalBasis required' })
}
const result = await gdprComplianceEngine.validateDataProcessing(
dataSubjectId,
processingPurpose,
dataCategories,
legalBasis
)
res.json({
compliance: result,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('GDPR compliance validation error:', error)
res.status(500).json({ error: 'GDPR validation failed' })
}
})
// Data subject rights endpoint
app.post('/api/gdpr/data-subject-request', async (req, res) => {
try {
const { dataSubjectId, right, requestDetails } = req.body
if (!dataSubjectId || !right) {
return res.status(400).json({ error: 'dataSubjectId and right required' })
}
const result = await gdprComplianceEngine.handleDataSubjectRequest(dataSubjectId, right, requestDetails)
res.json({
request: result,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Data subject request handling error:', error)
res.status(500).json({ error: 'Request handling failed' })
}
})
// Consent management endpoint
app.post('/api/gdpr/consent', async (req, res) => {
try {
const { dataSubjectId, purpose, consentGiven, consentText } = req.body
if (!dataSubjectId || !purpose) {
return res.status(400).json({ error: 'dataSubjectId and purpose required' })
}
const consentId = await gdprComplianceEngine.recordConsent(dataSubjectId, purpose, consentGiven, consentText)
res.json({
consentId,
message: consentGiven ? 'Consent recorded' : 'Consent denied',
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Consent management error:', error)
res.status(500).json({ error: 'Consent management failed' })
}
})
// Withdraw consent endpoint
app.delete('/api/gdpr/consent', async (req, res) => {
try {
const { dataSubjectId, purpose } = req.body
if (!dataSubjectId || !purpose) {
return res.status(400).json({ error: 'dataSubjectId and purpose required' })
}
const success = await gdprComplianceEngine.withdrawConsent(dataSubjectId, purpose)
if (success) {
res.json({
message: 'Consent withdrawn successfully',
timestamp: new Date().toISOString()
})
} else {
res.status(404).json({ error: 'Consent record not found' })
}
} catch (error) {
console.error('Consent withdrawal error:', error)
res.status(500).json({ error: 'Consent withdrawal failed' })
}
})
// GDPR compliance report endpoint
app.get('/api/gdpr/compliance-report', (req, res) => {
try {
const { startDate, endDate } = req.query
const start = startDate ? new Date(startDate as string).getTime() : Date.now() - 30 * 24 * 60 * 60 * 1000
const end = endDate ? new Date(endDate as string).getTime() : Date.now()
const report = gdprComplianceEngine.generateComplianceReport({ start, end })
res.json({
report,
period: { start, end },
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Compliance report generation error:', error)
res.status(500).json({ error: 'Report generation failed' })
}
})
console.log('GDPR Compliance Engine initialized')CCPA Compliance Framework
// CCPA compliance framework for California Consumer Privacy Act
interface CCPAComplianceConfig {
businessType: 'for_profit' | 'nonprofit' | 'government'
annualRevenue: number
dataCollection: {
personalInformation: boolean
sensitivePersonalInformation: boolean
biometricInformation: boolean
geolocationData: boolean
}
dataSharing: {
thirdPartySharing: boolean
saleOfPersonalInformation: boolean
crossContextBehavioralAdvertising: boolean
}
consumerRights: {
rightToKnow: boolean
rightToDelete: boolean
rightToOptOut: boolean
rightToCorrect: boolean
rightToDataPortability: boolean
rightToLimitUse: boolean
}
dataRetention: {
defaultPeriod: number
deletionGracePeriod: number
}
}
interface CCPAConsumerRequest {
id: string
consumerId: string
requestType: 'know' | 'delete' | 'opt_out' | 'correct' | 'portability' | 'limit_use'
requestDate: number
status: 'pending' | 'in_progress' | 'completed' | 'denied'
personalInformationCategories: string[]
responseData?: any
completionDate?: number
denialReason?: string
}
interface CCPAComplianceResult {
isCompliant: boolean
violations: string[]
warnings: string[]
requiredActions: string[]
consumerRights: string[]
retentionInfo: {
canRetain: boolean
retentionPeriod: number
deletionRequired: boolean
}
}
class CCPAComplianceFramework {
private config: CCPAComplianceConfig
private consumerRequests: Map<string, CCPAConsumerRequest> = new Map()
private dataInventory: Map<string, any> = new Map()
private optOutList: Set<string> = new Set()
constructor(config: CCPAComplianceConfig) {
this.config = config
this.initializeCCPAFramework()
}
async validateDataCollection(
consumerId: string,
dataCategories: string[],
collectionPurpose: string
): Promise<CCPAComplianceResult> {
const violations: string[] = []
const warnings: string[] = []
const requiredActions: string[] = []
const consumerRights: string[] = []
try {
// Check if business is subject to CCPA
if (!this.isBusinessSubjectToCCPA()) {
return {
isCompliant: true,
violations: [],
warnings: [],
requiredActions: [],
consumerRights: [],
retentionInfo: {
canRetain: true,
retentionPeriod: 0,
deletionRequired: false
}
}
}
// Validate data collection practices
const collectionCheck = this.validateCollectionPractices(dataCategories, collectionPurpose)
if (!collectionCheck.isValid) {
violations.push(`Invalid collection practice: ${collectionCheck.reason}`)
}
// Check data sharing compliance
const sharingCheck = this.validateDataSharing(dataCategories)
if (!sharingCheck.isCompliant) {
violations.push(`Data sharing violation: ${sharingCheck.reason}`)
}
// Validate consumer notice requirements
const noticeCheck = this.validateConsumerNotice(consumerId)
if (!noticeCheck.isCompliant) {
requiredActions.push('Provide required privacy notice to consumer')
}
// Check opt-out status
if (this.optOutList.has(consumerId)) {
violations.push('Consumer has opted out of data sales')
requiredActions.push('Honor consumer opt-out request')
}
// Determine applicable consumer rights
consumerRights.push(...this.determineConsumerRights(dataCategories))
// Validate data retention
const retentionCheck = this.validateRetentionPolicy(dataCategories)
// Create request record for tracking
const requestId = this.generateRequestId()
const consumerRequest: CCPAConsumerRequest = {
id: requestId,
consumerId,
requestType: 'know',
requestDate: Date.now(),
status: 'completed',
personalInformationCategories: dataCategories,
responseData: {
collectionPurpose,
legalBasis: 'Business purpose',
retentionPeriod: retentionCheck.retentionPeriod
},
completionDate: Date.now()
}
this.consumerRequests.set(requestId, consumerRequest)
return {
isCompliant: violations.length === 0,
violations,
warnings,
requiredActions,
consumerRights,
retentionInfo: retentionCheck
}
} catch (error) {
console.error('CCPA compliance validation failed:', error)
return {
isCompliant: false,
violations: ['Validation system error'],
warnings: [],
requiredActions: ['Contact system administrator'],
consumerRights: [],
retentionInfo: {
canRetain: false,
retentionPeriod: 0,
deletionRequired: true
}
}
}
}
private isBusinessSubjectToCCPA(): boolean {
// CCPA applies to for-profit businesses that:
// 1. Have annual gross revenue > $25M, OR
// 2. Buy/sell/share personal information of 100,000+ consumers/households, OR
// 3. Derive 50%+ of revenue from selling personal information
if (this.config.businessType !== 'for_profit') {
return false
}
return (
this.config.annualRevenue > 25000000 ||
this.config.dataSharing.saleOfPersonalInformation ||
this.config.dataCollection.personalInformation
)
}
private validateCollectionPractices(
dataCategories: string[],
purpose: string
): { isValid: boolean; reason?: string } {
// CCPA requires businesses to collect only necessary personal information
const sensitiveCategories = [
'social_security_number',
'drivers_license',
'financial_account',
'medical_information',
'biometric_data'
]
const hasSensitiveData = dataCategories.some(cat => sensitiveCategories.includes(cat))
if (hasSensitiveData && !this.isSensitiveDataNecessary(purpose)) {
return {
isValid: false,
reason: 'Sensitive data collection not justified by business purpose'
}
}
return { isValid: true }
}
private validateDataSharing(dataCategories: string[]): {
isCompliant: boolean
reason?: string
} {
// CCPA requires notice and opt-out for data sales
if (this.config.dataSharing.saleOfPersonalInformation) {
return {
isCompliant: false,
reason: 'Data sales require consumer notice and opt-out mechanism'
}
}
return { isCompliant: true }
}
private validateConsumerNotice(consumerId: string): {
isCompliant: boolean
missingNotices?: string[]
} {
// CCPA requires privacy notice at or before collection
const missingNotices: string[] = []
if (!this.hasProvidedPrivacyNotice(consumerId)) {
missingNotices.push('privacy_policy')
}
if (this.config.dataSharing.saleOfPersonalInformation && !this.hasProvidedOptOutNotice(consumerId)) {
missingNotices.push('opt_out_notice')
}
return {
isCompliant: missingNotices.length === 0,
missingNotices
}
}
private isSensitiveDataNecessary(purpose: string): boolean {
// Define when sensitive data collection is justified
const justifiedPurposes: Record<string, string[]> = {
'identity_verification': ['social_security_number', 'drivers_license'],
'financial_services': ['financial_account'],
'healthcare_services': ['medical_information'],
'security_authentication': ['biometric_data']
}
for (const [purposeKey, categories] of Object.entries(justifiedPurposes)) {
if (purpose.toLowerCase().includes(purposeKey.replace('_', ' '))) {
return true
}
}
return false
}
private hasProvidedPrivacyNotice(consumerId: string): boolean {
// In production, check if privacy notice was provided
// For demo, simulate based on consumer ID
return consumerId.startsWith('notice_provided_')
}
private hasProvidedOptOutNotice(consumerId: string): boolean {
// In production, check if opt-out notice was provided
// For demo, simulate based on consumer ID
return consumerId.startsWith('optout_notice_')
}
private determineConsumerRights(dataCategories: string[]): string[] {
const rights: string[] = []
if (this.config.consumerRights.rightToKnow) {
rights.push('Right to know what personal information is collected')
}
if (this.config.consumerRights.rightToDelete) {
rights.push('Right to delete personal information')
}
if (this.config.consumerRights.rightToOptOut && this.config.dataSharing.saleOfPersonalInformation) {
rights.push('Right to opt-out of the sale of personal information')
}
if (this.config.consumerRights.rightToCorrect) {
rights.push('Right to correct inaccurate personal information')
}
if (this.config.consumerRights.rightToDataPortability) {
rights.push('Right to data portability')
}
if (this.config.consumerRights.rightToLimitUse && this.hasSensitiveData(dataCategories)) {
rights.push('Right to limit the use of sensitive personal information')
}
return rights
}
private hasSensitiveData(dataCategories: string[]): boolean {
const sensitiveCategories = [
'social_security_number',
'drivers_license',
'financial_account',
'medical_information',
'biometric_data',
'geolocation_data'
]
return dataCategories.some(cat => sensitiveCategories.includes(cat))
}
private validateRetentionPolicy(dataCategories: string[]): {
canRetain: boolean
retentionPeriod: number
deletionRequired: boolean
} {
// CCPA requires deletion of personal information when no longer necessary
const hasPersonalData = dataCategories.includes('personal_information') ||
dataCategories.includes('contact_information')
if (!hasPersonalData) {
return {
canRetain: true,
retentionPeriod: 0,
deletionRequired: false
}
}
return {
canRetain: true,
retentionPeriod: this.config.dataRetention.defaultPeriod,
deletionRequired: false
}
}
private generateRequestId(): string {
return `ccpa_request_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}
private initializeCCPAFramework(): void {
console.log('CCPA Compliance Framework initialized')
}
// Consumer Rights Implementation
async handleConsumerRequest(
consumerId: string,
requestType: CCPAConsumerRequest['requestType'],
requestDetails?: any
): Promise<{
success: boolean
requestId: string
message: string
estimatedCompletion?: number
}> {
const requestId = this.generateRequestId()
const estimatedCompletion = Date.now() + 45 * 24 * 60 * 60 * 1000 // 45 days
const consumerRequest: CCPAConsumerRequest = {
id: requestId,
consumerId,
requestType,
requestDate: Date.now(),
status: 'pending',
personalInformationCategories: requestDetails?.categories || []
}
this.consumerRequests.set(requestId, consumerRequest)
// Process request based on type
switch (requestType) {
case 'know':
return this.processKnowRequest(consumerRequest)
case 'delete':
return this.processDeleteRequest(consumerRequest)
case 'opt_out':
return this.processOptOutRequest(consumerRequest)
case 'correct':
return this.processCorrectRequest(consumerRequest, requestDetails)
case 'portability':
return this.processPortabilityRequest(consumerRequest)
case 'limit_use':
return this.processLimitUseRequest(consumerRequest)
default:
return {
success: false,
requestId: '',
message: 'Unknown request type'
}
}
}
private async processKnowRequest(request: CCPAConsumerRequest): Promise<any> {
// CCPA Right to Know: Businesses must disclose personal information collected
request.status = 'in_progress'
// In production, gather personal information from all systems
const personalInfo = {
categories: request.personalInformationCategories,
collectionSources: ['Website', 'Mobile App', 'Customer Service'],
businessPurposes: ['Customer service', 'Marketing', 'Analytics'],
thirdPartyRecipients: this.config.dataSharing.thirdPartySharing ?
['Marketing partners', 'Analytics providers'] : [],
collectionDate: Date.now() - 30 * 24 * 60 * 60 * 1000
}
request.responseData = personalInfo
request.status = 'completed'
request.completionDate = Date.now()
return {
success: true,
requestId: request.id,
message: 'Right to Know request processed',
estimatedCompletion: request.completionDate
}
}
private async processDeleteRequest(request: CCPAConsumerRequest): Promise<any> {
// CCPA Right to Delete: Businesses must delete consumer's personal information
request.status = 'in_progress'
// In production, delete personal information from all systems
const deletionInfo = {
categoriesDeleted: request.personalInformationCategories,
deletionDate: Date.now(),
retentionExceptions: ['Legal compliance', 'Transaction completion']
}
request.responseData = deletionInfo
request.status = 'completed'
request.completionDate = Date.now()
return {
success: true,
requestId: request.id,
message: 'Right to Delete request processed',
estimatedCompletion: request.completionDate
}
}
private async processOptOutRequest(request: CCPAConsumerRequest): Promise<any> {
// CCPA Right to Opt-Out: Consumers can opt-out of data sales
this.optOutList.add(request.consumerId)
request.status = 'completed'
request.completionDate = Date.now()
return {
success: true,
requestId: request.id,
message: 'Opt-out request processed',
estimatedCompletion: request.completionDate
}
}
private async processCorrectRequest(request: CCPAConsumerRequest, corrections: any): Promise<any> {
// CCPA Right to Correct: Businesses must correct inaccurate personal information
request.status = 'in_progress'
// In production, update personal information in all systems
const correctionInfo = {
correctionsApplied: corrections,
correctionDate: Date.now()
}
request.responseData = correctionInfo
request.status = 'completed'
request.completionDate = Date.now()
return {
success: true,
requestId: request.id,
message: 'Right to Correct request processed',
estimatedCompletion: request.completionDate
}
}
private async processPortabilityRequest(request: CCPAConsumerRequest): Promise<any> {
// CCPA Right to Data Portability: Provide personal information in portable format
request.status = 'in_progress'
// In production, export personal information in machine-readable format
const exportData = {
personalInformation: {
contact: 'consumer@example.com',
preferences: ['marketing_opt_in'],
activity: ['website_visits', 'purchases']
},
exportFormat: 'JSON',
exportDate: Date.now()
}
request.responseData = exportData
request.status = 'completed'
request.completionDate = Date.now()
return {
success: true,
requestId: request.id,
message: 'Right to Data Portability request processed',
estimatedCompletion: request.completionDate
}
}
private async processLimitUseRequest(request: CCPAConsumerRequest): Promise<any> {
// CCPA Right to Limit Use: Limit use of sensitive personal information
request.status = 'in_progress'
// In production, restrict processing of sensitive data
const limitationInfo = {
sensitiveDataCategories: request.personalInformationCategories.filter(cat =>
['biometric', 'geolocation', 'health'].includes(cat)
),
limitationApplied: true,
limitationDate: Date.now()
}
request.responseData = limitationInfo
request.status = 'completed'
request.completionDate = Date.now()
return {
success: true,
requestId: request.id,
message: 'Right to Limit Use request processed',
estimatedCompletion: request.completionDate
}
}
// Opt-out management
isConsumerOptedOut(consumerId: string): boolean {
return this.optOutList.has(consumerId)
}
addConsumerOptOut(consumerId: string): void {
this.optOutList.add(consumerId)
}
removeConsumerOptOut(consumerId: string): void {
this.optOutList.delete(consumerId)
}
// Compliance reporting
generateCCPAReport(): {
totalRequests: number
completedRequests: number
pendingRequests: number
deniedRequests: number
optOutCount: number
dataCategories: string[]
businessPurposes: string[]
} {
const requests = Array.from(this.consumerRequests.values())
return {
totalRequests: requests.length,
completedRequests: requests.filter(r => r.status === 'completed').length,
pendingRequests: requests.filter(r => r.status === 'pending').length,
deniedRequests: requests.filter(r => r.status === 'denied').length,
optOutCount: this.optOutList.size,
dataCategories: [...new Set(requests.flatMap(r => r.personalInformationCategories))],
businessPurposes: this.config.dataCollection.personalInformation ?
['Customer service', 'Marketing', 'Analytics', 'Fraud prevention'] : []
}
}
}
// Initialize CCPA compliance framework
const ccpaConfig: CCPAComplianceConfig = {
businessType: 'for_profit',
annualRevenue: 50000000,
dataCollection: {
personalInformation: true,
sensitivePersonalInformation: false,
biometricInformation: false,
geolocationData: true
},
dataSharing: {
thirdPartySharing: true,
saleOfPersonalInformation: false,
crossContextBehavioralAdvertising: true
},
consumerRights: {
rightToKnow: true,
rightToDelete: true,
rightToOptOut: true,
rightToCorrect: true,
rightToDataPortability: true,
rightToLimitUse: true
},
dataRetention: {
defaultPeriod: 365,
deletionGracePeriod: 30
}
}
const ccpaComplianceFramework = new CCPAComplianceFramework(ccpaConfig)
// CCPA compliance validation endpoint
app.post('/api/ccpa/validate-collection', async (req, res) => {
try {
const { consumerId, dataCategories, collectionPurpose } = req.body
if (!consumerId || !dataCategories || !collectionPurpose) {
return res.status(400).json({ error: 'consumerId, dataCategories, and collectionPurpose required' })
}
const result = await ccpaComplianceFramework.validateDataCollection(
consumerId,
dataCategories,
collectionPurpose
)
res.json({
compliance: result,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('CCPA compliance validation error:', error)
res.status(500).json({ error: 'CCPA validation failed' })
}
})
// Consumer rights request endpoint
app.post('/api/ccpa/consumer-request', async (req, res) => {
try {
const { consumerId, requestType, requestDetails } = req.body
if (!consumerId || !requestType) {
return res.status(400).json({ error: 'consumerId and requestType required' })
}
const result = await ccpaComplianceFramework.handleConsumerRequest(consumerId, requestType, requestDetails)
res.json({
request: result,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Consumer request handling error:', error)
res.status(500).json({ error: 'Request handling failed' })
}
})
// Opt-out management endpoints
app.post('/api/ccpa/opt-out', async (req, res) => {
try {
const { consumerId } = req.body
if (!consumerId) {
return res.status(400).json({ error: 'consumerId required' })
}
ccpaComplianceFramework.addConsumerOptOut(consumerId)
res.json({
message: 'Consumer opted out successfully',
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Opt-out processing error:', error)
res.status(500).json({ error: 'Opt-out processing failed' })
}
})
app.delete('/api/ccpa/opt-out', async (req, res) => {
try {
const { consumerId } = req.body
if (!consumerId) {
return res.status(400).json({ error: 'consumerId required' })
}
ccpaComplianceFramework.removeConsumerOptOut(consumerId)
res.json({
message: 'Consumer opt-in processed successfully',
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Opt-in processing error:', error)
res.status(500).json({ error: 'Opt-in processing failed' })
}
})
// CCPA compliance report endpoint
app.get('/api/ccpa/compliance-report', (req, res) => {
try {
const report = ccpaComplianceFramework.generateCCPAReport()
res.json({
report,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('CCPA report generation error:', error)
res.status(500).json({ error: 'Report generation failed' })
}
})
console.log('CCPA Compliance Framework initialized')Protection Mechanisms
Successful implementation requires understanding the technical landscape and choosing appropriate strategies.
Implementation Approaches
Modern Solutions
- Cloud-native architectures
- Microservices integration
- Real-time processing capabilities
- Automated scaling mechanisms
Data Privacy in API Validation Architecture
Implementation Strategies {#implementation-strategies}
Deploy privacy-compliant validation systems.
class PrivacyCompliantValidation {
private gdprEngine: GDPRComplianceEngine
private ccpaEngine: CCPAComplianceEngine
constructor() {
this.gdprEngine = new GDPRComplianceEngine()
this.ccpaEngine = new CCPAComplianceEngine()
}
async validate(data: any, userRegion: string): Promise<{
valid: boolean
privacyCompliant: boolean
warnings: string[]
}> {
const engine = userRegion === 'EU' ? this.gdprEngine : this.ccpaEngine
const compliance = await engine.validateProcessing(data)
return {
valid: true,
privacyCompliant: compliance.isCompliant,
warnings: compliance.warnings
}
}
}Monitoring and Detection {#monitoring-and-detection}
Track privacy compliance and data handling.
Key Metrics:
- Data retention compliance rate
- Consent tracking accuracy
- Data subject request response time
- Privacy incident count
- Audit log completeness
Incident Response Planning {#incident-response-planning}
Respond to privacy incidents.
const PRIVACY_INCIDENT_PLAYBOOKS = {
'unauthorized_access': {
severity: 'critical',
actions: ['Revoke access', 'Notify DPO', 'Notify affected users'],
reportingDeadline: 72 // hours (GDPR requirement)
},
'data_breach': {
severity: 'critical',
actions: ['Contain breach', 'Assess impact', 'Notify authorities', 'Notify users'],
reportingDeadline: 72
},
'retention_violation': {
severity: 'medium',
actions: ['Delete expired data', 'Review retention policy', 'Update processes'],
reportingDeadline: null
}
}Compliance and Best Practices {#compliance-and-best-practices}
Maintain ongoing privacy compliance.
Best Practices:
- Regular privacy impact assessments
- Data minimization in validation
- Transparent data processing notices
- Documented consent mechanisms
- Privacy-by-design architecture
- Regular compliance audits
Conclusion {#conclusion}
Data privacy in API validation requires implementing GDPR/CCPA frameworks, data minimization, consent management, retention policies, and data subject rights. Success depends on privacy-by-design architecture, comprehensive audit trails, automated compliance checks, and proactive privacy incident management.
Key success factors include understanding applicable regulations, implementing privacy controls at API level, maintaining detailed processing records, providing data subject rights mechanisms, and conducting regular privacy audits.
Validate data privacy-compliantly with our APIs, designed to meet GDPR, CCPA, and global privacy requirements while maintaining performance and user experience.