Tillbaka till bloggen
Compliance

PIPEDA and Bill C-27: The Future of Privacy in Canada

GetCookies-teametNovember 17, 202511 min läsning
CanadaPIPEDABill C-27CPPA

TLDR: Canada's CPPA will transform PIPEDA into an enforcement-focused regime with GDPR-level fines.

Read full summary

Analysis of Canada's proposed Consumer Privacy Protection Act and its impact on consent management. Covers the shift from ombudsman to tribunal model, new consent requirements, and preparing for penalties up to 5% of global revenue.

Summary by Claude AI


title: "PIPEDA and CPPA: The Complete Guide to Canadian Privacy Law for Digital Businesses" slug: "pipeda-cppa-canada-privacy" excerpt: "Navigate Canada's evolving privacy landscape from PIPEDA to the proposed Consumer Privacy Protection Act (CPPA). Learn about compliance requirements, consent models, algorithmic transparency, and enforcement mechanisms." category: "Compliance" tags: ["PIPEDA", "CPPA", "Canada privacy", "Bill C-27", "consent", "data protection", "compliance"] publishedAt: "2025-01-13" readTime: "20 min read"

What is Bill C-27 and how will it change Canadian privacy law? Bill C-27 is proposed federal legislation that would enact the Consumer Privacy Protection Act (CPPA), effectively modernizing and replacing the privacy provisions of PIPEDA. The CPPA introduces penalties up to 5% of global revenue (or $25 million CAD, whichever is greater), creates a new Data Protection Tribunal, requires algorithmic transparency, and grants individuals a private right of action to sue for privacy violations.

For over two decades, the Personal Information Protection and Electronic Documents Act (PIPEDA) has governed how private-sector organizations collect, use, and disclose personal information in Canada. But the digital age has evolved dramatically since PIPEDA's enactment in 2000, and Canadian privacy law is undergoing its most significant transformation yet. Understanding both the current requirements and the coming changes is essential for any organization doing business in Canada.

Understanding PIPEDA: The Current Framework

PIPEDA applies to private-sector organizations that collect, use, or disclose personal information in the course of commercial activities. It's built around 10 fair information principles that have served as the foundation of Canadian privacy law.

The 10 PIPEDA Principles

These principles form the backbone of Canadian privacy compliance:

// PIPEDA's 10 Fair Information Principles
interface PIPEDAPrinciples {
  accountability: {
    description: 'Designate individual(s) responsible for compliance';
    requirements: string[];
  };
  identifyingPurposes: {
    description: 'Identify purposes for collection before or at time of collection';
    requirements: string[];
  };
  consent: {
    description: 'Obtain meaningful consent for collection, use, disclosure';
    requirements: string[];
  };
  limitingCollection: {
    description: 'Limit collection to what is necessary for identified purposes';
    requirements: string[];
  };
  limitingUse: {
    description: 'Use or disclose only for purposes for which it was collected';
    requirements: string[];
  };
  accuracy: {
    description: 'Keep personal information accurate, complete, up-to-date';
    requirements: string[];
  };
  safeguards: {
    description: 'Protect personal information with appropriate security';
    requirements: string[];
  };
  openness: {
    description: 'Make privacy policies and practices readily available';
    requirements: string[];
  };
  individualAccess: {
    description: 'Provide individuals access to their personal information';
    requirements: string[];
  };
  challengingCompliance: {
    description: 'Provide ability to challenge compliance and address complaints';
    requirements: string[];
  };
}

const pipedaPrinciples: PIPEDAPrinciples = {
  accountability: {
    description: 'Designate individual(s) responsible for compliance',
    requirements: [
      'Appoint Privacy Officer or equivalent',
      'Implement privacy policies and procedures',
      'Train employees on privacy obligations',
      'Ensure third parties provide equivalent protection',
    ],
  },
  identifyingPurposes: {
    description: 'Identify purposes for collection before or at time of collection',
    requirements: [
      'Document specific purposes for each data element',
      'Communicate purposes to individuals clearly',
      'Limit purposes to what reasonable person would expect',
      'Update purpose statements when purposes change',
    ],
  },
  consent: {
    description: 'Obtain meaningful consent for collection, use, disclosure',
    requirements: [
      'Consent must be informed and voluntary',
      'Form of consent must be appropriate to sensitivity',
      'Individuals may withdraw consent at any time',
      'Explain consequences of refusing consent',
    ],
  },
  limitingCollection: {
    description: 'Limit collection to what is necessary for identified purposes',
    requirements: [
      'Only collect information needed for stated purposes',
      'Use fair and lawful means of collection',
      'Do not deceive or mislead about collection',
    ],
  },
  limitingUse: {
    description: 'Use or disclose only for purposes for which it was collected',
    requirements: [
      'Do not use for new purposes without fresh consent',
      'Retain only as long as necessary',
      'Destroy, erase, or anonymize when no longer needed',
    ],
  },
  accuracy: {
    description: 'Keep personal information accurate, complete, up-to-date',
    requirements: [
      'Accuracy proportional to use of information',
      'Do not routinely update unless necessary',
      'Allow individuals to request corrections',
    ],
  },
  safeguards: {
    description: 'Protect personal information with appropriate security',
    requirements: [
      'Physical, organizational, and technological measures',
      'Safeguards proportional to sensitivity',
      'Protect against loss, theft, unauthorized access',
      'Train employees on security obligations',
    ],
  },
  openness: {
    description: 'Make privacy policies and practices readily available',
    requirements: [
      'Publish privacy policy in accessible format',
      'Describe types of information held',
      'Explain how information is used',
      'Provide contact for Privacy Officer',
    ],
  },
  individualAccess: {
    description: 'Provide individuals access to their personal information',
    requirements: [
      'Respond within 30 days of request',
      'Provide information in understandable form',
      'Explain any use or disclosure',
      'Minimal or no cost to individual',
    ],
  },
  challengingCompliance: {
    description: 'Provide ability to challenge compliance and address complaints',
    requirements: [
      'Establish complaint handling procedures',
      'Investigate all complaints',
      'Take corrective action where appropriate',
      'Inform individuals of complaint options',
    ],
  },
};

PIPEDA Consent Framework

Consent under PIPEDA must be meaningful, which requires clear communication about what data is collected and why:

// PIPEDA consent management implementation
interface PIPEDAConsentConfig {
  organizationName: string;
  privacyOfficer: {
    name: string;
    email: string;
    phone?: string;
  };
  dataCollectionPurposes: DataPurpose[];
  sensitiveDataCategories: string[];
}

interface DataPurpose {
  id: string;
  name: string;
  description: string;
  dataElements: string[];
  isSensitive: boolean;
  consentType: 'express' | 'implied' | 'opt-out';
  retentionPeriod: string;
  thirdPartySharing: boolean;
  thirdParties?: string[];
}

class PIPEDAConsentManager {
  private config: PIPEDAConsentConfig;
  private consentRecords: Map<string, UserConsentRecord> = new Map();

  constructor(config: PIPEDAConsentConfig) {
    this.config = config;
  }

  // Determine appropriate consent type based on PIPEDA guidelines
  determineConsentType(purpose: DataPurpose): 'express' | 'implied' | 'opt-out' {
    // Express consent required for:
    // - Sensitive personal information
    // - Personal information collected for unexpected purposes
    // - Personal information shared with third parties
    if (purpose.isSensitive) {
      return 'express';
    }

    if (purpose.thirdPartySharing) {
      return 'express';
    }

    // Implied consent may be appropriate when:
    // - Information is not sensitive
    // - Collection is clearly necessary for product/service
    // - Individual voluntarily provides information
    const impliedAllowedPurposes = [
      'service_delivery',
      'account_management',
      'transaction_processing',
    ];

    if (impliedAllowedPurposes.includes(purpose.id)) {
      return 'implied';
    }

    // Opt-out for:
    // - Marketing communications (with existing relationship)
    // - Statistical/research purposes (with safeguards)
    if (purpose.id === 'marketing' || purpose.id === 'analytics') {
      return 'opt-out';
    }

    return 'express';
  }

  async obtainConsent(
    userId: string,
    purposes: string[],
    method: ConsentMethod
  ): Promise<ConsentResult> {
    const results: PurposeConsentResult[] = [];
    const timestamp = new Date();

    for (const purposeId of purposes) {
      const purpose = this.config.dataCollectionPurposes.find(p => p.id === purposeId);
      if (!purpose) continue;

      const requiredType = this.determineConsentType(purpose);

      // Validate that consent method matches required type
      if (requiredType === 'express' && method.type !== 'express') {
        results.push({
          purposeId,
          granted: false,
          reason: 'Express consent required but not obtained',
        });
        continue;
      }

      results.push({
        purposeId,
        granted: true,
        consentType: requiredType,
        method: method.type,
      });
    }

    // Store consent record
    const record: UserConsentRecord = {
      userId,
      timestamp,
      purposes: results,
      method,
      withdrawable: true,
    };

    this.consentRecords.set(userId, record);

    return {
      success: results.every(r => r.granted),
      results,
      record,
    };
  }

  async withdrawConsent(userId: string, purposes?: string[]): Promise<void> {
    const record = this.consentRecords.get(userId);
    if (!record) return;

    if (purposes) {
      // Withdraw specific purposes
      record.purposes = record.purposes.map(p => {
        if (purposes.includes(p.purposeId)) {
          return { ...p, granted: false, withdrawnAt: new Date() };
        }
        return p;
      });
    } else {
      // Withdraw all consents
      record.purposes = record.purposes.map(p => ({
        ...p,
        granted: false,
        withdrawnAt: new Date(),
      }));
    }

    record.lastModified = new Date();
    this.consentRecords.set(userId, record);

    // Trigger data handling updates
    await this.handleConsentWithdrawal(userId, purposes);
  }

  private async handleConsentWithdrawal(userId: string, purposes?: string[]): Promise<void> {
    // Stop processing data for withdrawn purposes
    // This may involve:
    // 1. Updating marketing preferences
    // 2. Stopping analytics tracking
    // 3. Removing from third-party sharing
    // 4. Potentially deleting data if no other lawful purpose exists

    console.log(`Processing consent withdrawal for user ${userId}`);
  }

  generatePrivacyNotice(purposes: DataPurpose[]): string {
    const notice = `
# Privacy Notice - ${this.config.organizationName}

## Who We Are
${this.config.organizationName} is committed to protecting your personal information
in accordance with the Personal Information Protection and Electronic Documents Act (PIPEDA).

## Privacy Officer Contact
**Name:** ${this.config.privacyOfficer.name}
**Email:** ${this.config.privacyOfficer.email}
${this.config.privacyOfficer.phone ? `**Phone:** ${this.config.privacyOfficer.phone}` : ''}

## Information We Collect and Why

${purposes.map(p => `
### ${p.name}
${p.description}

**Data collected:** ${p.dataElements.join(', ')}
**Consent type:** ${p.consentType}
**Retention period:** ${p.retentionPeriod}
${p.thirdPartySharing ? `**Shared with:** ${p.thirdParties?.join(', ')}` : '**Not shared with third parties**'}
`).join('\n')}

## Your Rights
Under PIPEDA, you have the right to:
- Access your personal information
- Request correction of inaccurate information
- Withdraw consent (subject to legal/contractual restrictions)
- File a complaint with the Privacy Commissioner of Canada

## How to Exercise Your Rights
Contact our Privacy Officer at ${this.config.privacyOfficer.email}

Last updated: ${new Date().toISOString().split('T')[0]}
`;

    return notice;
  }
}

interface ConsentMethod {
  type: 'express' | 'implied' | 'opt-out';
  mechanism: 'checkbox' | 'signature' | 'click' | 'continued_use';
  timestamp: Date;
  evidenceId?: string;
}

interface PurposeConsentResult {
  purposeId: string;
  granted: boolean;
  consentType?: string;
  method?: string;
  reason?: string;
  withdrawnAt?: Date;
}

interface UserConsentRecord {
  userId: string;
  timestamp: Date;
  purposes: PurposeConsentResult[];
  method: ConsentMethod;
  withdrawable: boolean;
  lastModified?: Date;
}

interface ConsentResult {
  success: boolean;
  results: PurposeConsentResult[];
  record: UserConsentRecord;
}

The Consumer Privacy Protection Act (CPPA): What's Changing

Bill C-27 represents a fundamental shift in Canadian privacy law. Here's what organizations need to prepare for.

Key CPPA Changes from PIPEDA

Aspect PIPEDA CPPA
Maximum penalties Naming and shaming, compliance orders Up to 5% global revenue or $25M CAD
Enforcement body Privacy Commissioner (ombudsman model) Privacy Commissioner + Data Protection Tribunal
Private right of action None Individuals can sue for damages
Algorithmic transparency Not addressed Right to explanation of automated decisions
Data mobility Not addressed Right to data portability
De-identification Guidance only Specific legal requirements
Minors' data General sensitivity Enhanced protections for children
Consent exceptions Limited Expanded legitimate interest provisions

CPPA Compliance Framework

// CPPA compliance management system
interface CPPAComplianceConfig {
  organization: OrganizationInfo;
  privacyProgram: PrivacyProgramConfig;
  dataInventory: DataInventoryItem[];
  automatedDecisionSystems: AutomatedDecisionSystem[];
}

interface OrganizationInfo {
  name: string;
  industry: string;
  size: 'small' | 'medium' | 'large';
  annualRevenue?: number;
  internationalTransfers: boolean;
}

interface PrivacyProgramConfig {
  privacyOfficer: ContactInfo;
  privacyPolicy: string;
  retentionSchedule: RetentionPolicy[];
  securityMeasures: SecurityMeasure[];
  breachProcedures: BreachResponsePlan;
}

interface DataInventoryItem {
  dataCategory: string;
  dataElements: string[];
  sources: string[];
  purposes: string[];
  lawfulBasis: CPPALawfulBasis;
  retention: string;
  crossBorderTransfers: CrossBorderTransfer[];
  automatedProcessing: boolean;
}

type CPPALawfulBasis =
  | 'consent'
  | 'legitimate_interest'
  | 'contract_performance'
  | 'legal_obligation'
  | 'vital_interests'
  | 'publicly_available';

class CPPAComplianceManager {
  private config: CPPAComplianceConfig;
  private consentRecords: Map<string, CPPAConsentRecord[]> = new Map();
  private accessRequests: Map<string, AccessRequest[]> = new Map();

  constructor(config: CPPAComplianceConfig) {
    this.config = config;
    this.validateDataInventory();
  }

  private validateDataInventory(): void {
    for (const item of this.config.dataInventory) {
      // CPPA requires clear lawful basis for each processing activity
      if (!item.lawfulBasis) {
        throw new Error(`Missing lawful basis for data category: ${item.dataCategory}`);
      }

      // Validate consent is not being used where inappropriate
      if (item.lawfulBasis === 'consent') {
        this.validateConsentAppropriate(item);
      }

      // Validate legitimate interest has been properly assessed
      if (item.lawfulBasis === 'legitimate_interest') {
        this.validateLegitimateInterest(item);
      }
    }
  }

  private validateConsentAppropriate(item: DataInventoryItem): void {
    // CPPA restricts use of consent in certain situations
    // e.g., cannot make consent a condition of service provision
    // unless data is truly necessary for the service
    const essentialPurposes = ['service_delivery', 'transaction_processing', 'account_management'];

    if (item.purposes.some(p => essentialPurposes.includes(p))) {
      console.warn(`Consider using contract_performance instead of consent for essential purposes`);
    }
  }

  private validateLegitimateInterest(item: DataInventoryItem): void {
    // CPPA requires documented legitimate interest assessment
    // Similar to GDPR's Legitimate Interest Assessment
    console.log(`Legitimate interest requires documented assessment for: ${item.dataCategory}`);
  }

  // CPPA Algorithmic Transparency Requirements
  async provideAlgorithmicExplanation(
    userId: string,
    decisionId: string
  ): Promise<AlgorithmicExplanation> {
    // Find the automated decision system that made this decision
    const decision = await this.findDecision(decisionId);
    if (!decision) {
      throw new Error('Decision not found');
    }

    const system = this.config.automatedDecisionSystems.find(
      s => s.id === decision.systemId
    );

    if (!system) {
      throw new Error('Automated decision system not found');
    }

    // CPPA requires organizations to explain:
    // 1. That an automated decision was made
    // 2. The type of personal information used
    // 3. Why the system made that particular decision
    // 4. How to challenge the decision

    return {
      decisionId,
      timestamp: decision.timestamp,
      systemDescription: system.description,
      dataUsed: decision.inputData.map(d => ({
        category: d.category,
        description: d.description,
        // Do not reveal actual values to prevent gaming
      })),
      decisionFactors: decision.factors.map(f => ({
        factor: f.name,
        impact: f.impact, // 'positive', 'negative', 'neutral'
        weight: this.categorizeWeight(f.weight), // 'high', 'medium', 'low'
      })),
      outcome: decision.outcome,
      humanReviewAvailable: system.humanReviewAvailable,
      challengeProcess: {
        description: 'You may request human review of this decision',
        contactEmail: this.config.privacyProgram.privacyOfficer.email,
        deadline: '30 days from decision date',
      },
    };
  }

  private async findDecision(decisionId: string): Promise<AutomatedDecision | null> {
    // In production, fetch from database
    return null;
  }

  private categorizeWeight(weight: number): 'high' | 'medium' | 'low' {
    if (weight > 0.6) return 'high';
    if (weight > 0.3) return 'medium';
    return 'low';
  }

  // CPPA Data Portability Implementation
  async generatePortableData(userId: string): Promise<PortableDataPackage> {
    // CPPA grants individuals the right to data portability
    // Data must be provided in a commonly used, machine-readable format

    const userData = await this.collectUserData(userId);

    return {
      exportDate: new Date(),
      format: 'json',
      schema: 'https://schema.org/Person',
      data: {
        personalInfo: this.formatPersonalInfo(userData.personalInfo),
        accountData: this.formatAccountData(userData.accountData),
        activityData: this.formatActivityData(userData.activityData),
        preferences: userData.preferences,
        consentHistory: this.formatConsentHistory(userId),
      },
      metadata: {
        organization: this.config.organization.name,
        exportedBy: 'CPPA Data Portability System',
        dataCategories: Object.keys(userData),
        retentionInfo: 'This export contains data retained as of the export date',
      },
    };
  }

  private async collectUserData(userId: string): Promise<UserDataCollection> {
    // Aggregate user data from all systems
    return {
      personalInfo: {},
      accountData: {},
      activityData: [],
      preferences: {},
    };
  }

  private formatPersonalInfo(info: Record<string, any>): Record<string, any> {
    // Format according to schema.org standards
    return info;
  }

  private formatAccountData(data: Record<string, any>): Record<string, any> {
    return data;
  }

  private formatActivityData(data: any[]): any[] {
    return data;
  }

  private formatConsentHistory(userId: string): ConsentHistoryEntry[] {
    const records = this.consentRecords.get(userId) || [];
    return records.map(r => ({
      date: r.timestamp,
      action: r.action,
      purposes: r.purposes,
      method: r.method,
    }));
  }

  // CPPA De-identification Requirements
  validateDeidentification(data: any[], method: DeidentificationMethod): DeidentificationReport {
    // CPPA has specific requirements for de-identification
    // Must use technical and administrative measures
    // Must ensure reasonable expectation that individual cannot be identified

    const risks: string[] = [];
    const recommendations: string[] = [];

    // Check for direct identifiers
    const directIdentifiers = this.findDirectIdentifiers(data);
    if (directIdentifiers.length > 0) {
      risks.push(`Direct identifiers found: ${directIdentifiers.join(', ')}`);
      recommendations.push('Remove or hash all direct identifiers');
    }

    // Check for quasi-identifiers that could enable re-identification
    const quasiIdentifiers = this.assessQuasiIdentifiers(data);
    if (quasiIdentifiers.riskLevel === 'high') {
      risks.push('High re-identification risk from quasi-identifier combination');
      recommendations.push('Apply k-anonymity with k >= 5');
      recommendations.push('Consider generalization of location data');
    }

    // Assess method appropriateness
    if (method.type === 'pseudonymization') {
      risks.push('Pseudonymization alone does not meet CPPA de-identification standard');
      recommendations.push('Use true anonymization techniques');
    }

    return {
      compliant: risks.length === 0,
      method: method.type,
      risks,
      recommendations,
      assessmentDate: new Date(),
      nextReviewDate: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
    };
  }

  private findDirectIdentifiers(data: any[]): string[] {
    const directIdentifierPatterns = [
      { field: 'email', pattern: /@/ },
      { field: 'phone', pattern: /^\+?[\d\s-()]+$/ },
      { field: 'sin', pattern: /^\d{3}-?\d{3}-?\d{3}$/ }, // Canadian SIN
      { field: 'name', pattern: /^[A-Za-z]+\s+[A-Za-z]+/ },
    ];

    // Check sample of data for patterns
    const found: string[] = [];
    // Implementation would check actual data
    return found;
  }

  private assessQuasiIdentifiers(data: any[]): QuasiIdentifierAssessment {
    // Quasi-identifiers include: age, gender, postal code, etc.
    // Combinations can enable re-identification
    return {
      identifiers: ['age', 'gender', 'postal_code'],
      riskLevel: 'medium',
      uniqueRecords: 0,
      kAnonymity: 5,
    };
  }
}

interface AutomatedDecisionSystem {
  id: string;
  name: string;
  description: string;
  purpose: string;
  dataInputs: string[];
  outputType: string;
  humanReviewAvailable: boolean;
  impactLevel: 'low' | 'medium' | 'high';
}

interface AutomatedDecision {
  id: string;
  systemId: string;
  timestamp: Date;
  inputData: { category: string; description: string }[];
  factors: { name: string; impact: string; weight: number }[];
  outcome: string;
}

interface AlgorithmicExplanation {
  decisionId: string;
  timestamp: Date;
  systemDescription: string;
  dataUsed: { category: string; description: string }[];
  decisionFactors: { factor: string; impact: string; weight: string }[];
  outcome: string;
  humanReviewAvailable: boolean;
  challengeProcess: {
    description: string;
    contactEmail: string;
    deadline: string;
  };
}

interface PortableDataPackage {
  exportDate: Date;
  format: string;
  schema: string;
  data: Record<string, any>;
  metadata: {
    organization: string;
    exportedBy: string;
    dataCategories: string[];
    retentionInfo: string;
  };
}

interface UserDataCollection {
  personalInfo: Record<string, any>;
  accountData: Record<string, any>;
  activityData: any[];
  preferences: Record<string, any>;
}

interface ConsentHistoryEntry {
  date: Date;
  action: string;
  purposes: string[];
  method: string;
}

interface DeidentificationMethod {
  type: 'anonymization' | 'pseudonymization' | 'aggregation';
  techniques: string[];
}

interface DeidentificationReport {
  compliant: boolean;
  method: string;
  risks: string[];
  recommendations: string[];
  assessmentDate: Date;
  nextReviewDate: Date;
}

interface QuasiIdentifierAssessment {
  identifiers: string[];
  riskLevel: 'low' | 'medium' | 'high';
  uniqueRecords: number;
  kAnonymity: number;
}

interface CPPAConsentRecord {
  timestamp: Date;
  action: string;
  purposes: string[];
  method: string;
}

interface ContactInfo {
  name: string;
  email: string;
  phone?: string;
}

interface RetentionPolicy {
  dataCategory: string;
  retentionPeriod: string;
  deletionMethod: string;
}

interface SecurityMeasure {
  type: string;
  description: string;
  implemented: boolean;
}

interface BreachResponsePlan {
  notificationThreshold: string;
  notificationTimeline: string;
  internalProcedures: string[];
  regulatorContact: string;
}

interface CrossBorderTransfer {
  destination: string;
  mechanism: string;
  safeguards: string[];
}

interface AccessRequest {
  id: string;
  userId: string;
  requestDate: Date;
  status: string;
}

CPPA Penalty Framework

The CPPA introduces serious financial consequences for non-compliance:

// CPPA penalty calculation framework
interface CPPAPenaltyFactors {
  violationType: ViolationType;
  severity: 'minor' | 'serious' | 'very_serious';
  duration: number; // days
  affectedIndividuals: number;
  organizationSize: 'small' | 'medium' | 'large';
  globalRevenue: number;
  previousViolations: number;
  cooperationLevel: 'full' | 'partial' | 'none';
  mitigationEfforts: boolean;
  intentional: boolean;
}

type ViolationType =
  | 'collection_without_consent'
  | 'failure_to_safeguard'
  | 'unauthorized_disclosure'
  | 'failure_to_respond_access_request'
  | 'algorithmic_transparency_failure'
  | 'data_portability_failure'
  | 'deidentification_failure'
  | 'minors_data_violation';

class CPPAPenaltyCalculator {
  private readonly MAX_PENALTY_PERCENTAGE = 0.05; // 5% of global revenue
  private readonly MAX_PENALTY_ABSOLUTE = 25000000; // $25M CAD

  calculatePotentialPenalty(factors: CPPAPenaltyFactors): PenaltyEstimate {
    const baseAmount = this.calculateBaseAmount(factors);
    const adjustedAmount = this.applyAdjustments(baseAmount, factors);
    const cappedAmount = this.applyCap(adjustedAmount, factors.globalRevenue);

    return {
      estimatedPenalty: cappedAmount,
      breakdown: {
        baseAmount,
        adjustedAmount,
        cappedAmount,
        percentageOfRevenue: (cappedAmount / factors.globalRevenue) * 100,
      },
      factors: this.explainFactors(factors),
      mitigationOpportunities: this.identifyMitigation(factors),
    };
  }

  private calculateBaseAmount(factors: CPPAPenaltyFactors): number {
    // Base amounts by violation type
    const baseAmounts: Record<ViolationType, number> = {
      collection_without_consent: 500000,
      failure_to_safeguard: 1000000,
      unauthorized_disclosure: 2000000,
      failure_to_respond_access_request: 100000,
      algorithmic_transparency_failure: 500000,
      data_portability_failure: 250000,
      deidentification_failure: 750000,
      minors_data_violation: 2500000,
    };

    return baseAmounts[factors.violationType] || 500000;
  }

  private applyAdjustments(base: number, factors: CPPAPenaltyFactors): number {
    let adjusted = base;

    // Severity multiplier
    const severityMultipliers = {
      minor: 0.5,
      serious: 1.0,
      very_serious: 2.0,
    };
    adjusted *= severityMultipliers[factors.severity];

    // Duration factor
    if (factors.duration > 30) {
      adjusted *= 1 + (factors.duration / 365);
    }

    // Affected individuals scale
    if (factors.affectedIndividuals > 10000) {
      adjusted *= 1.5;
    }
    if (factors.affectedIndividuals > 100000) {
      adjusted *= 2.0;
    }

    // Recidivism
    adjusted *= 1 + (factors.previousViolations * 0.5);

    // Intentional violation
    if (factors.intentional) {
      adjusted *= 2.0;
    }

    // Cooperation discount
    if (factors.cooperationLevel === 'full') {
      adjusted *= 0.7;
    } else if (factors.cooperationLevel === 'partial') {
      adjusted *= 0.85;
    }

    // Mitigation discount
    if (factors.mitigationEfforts) {
      adjusted *= 0.8;
    }

    return adjusted;
  }

  private applyCap(amount: number, globalRevenue: number): number {
    const percentageCap = globalRevenue * this.MAX_PENALTY_PERCENTAGE;
    const effectiveCap = Math.min(percentageCap, this.MAX_PENALTY_ABSOLUTE);
    return Math.min(amount, effectiveCap);
  }

  private explainFactors(factors: CPPAPenaltyFactors): string[] {
    const explanations: string[] = [];

    explanations.push(`Violation type: ${factors.violationType.replace(/_/g, ' ')}`);
    explanations.push(`Severity level: ${factors.severity}`);
    explanations.push(`Duration of violation: ${factors.duration} days`);
    explanations.push(`Affected individuals: ${factors.affectedIndividuals.toLocaleString()}`);

    if (factors.intentional) {
      explanations.push('Violation was intentional (2x multiplier)');
    }

    if (factors.previousViolations > 0) {
      explanations.push(`Previous violations: ${factors.previousViolations} (recidivism factor)`);
    }

    return explanations;
  }

  private identifyMitigation(factors: CPPAPenaltyFactors): string[] {
    const opportunities: string[] = [];

    if (factors.cooperationLevel !== 'full') {
      opportunities.push('Full cooperation with investigation could reduce penalty by up to 30%');
    }

    if (!factors.mitigationEfforts) {
      opportunities.push('Implementing remediation measures could reduce penalty by up to 20%');
    }

    opportunities.push('Self-reporting violations may be viewed favorably by the Tribunal');
    opportunities.push('Demonstrating privacy program improvements can influence penalty assessment');

    return opportunities;
  }
}

interface PenaltyEstimate {
  estimatedPenalty: number;
  breakdown: {
    baseAmount: number;
    adjustedAmount: number;
    cappedAmount: number;
    percentageOfRevenue: number;
  };
  factors: string[];
  mitigationOpportunities: string[];
}

Provincial Privacy Laws to Consider

Canada's privacy landscape includes provincial laws that may apply:

Province Law Key Features
British Columbia PIPA Similar to PIPEDA, covers BC private sector
Alberta PIPA Alberta-specific, employee privacy provisions
Quebec Law 25 Most stringent, GDPR-like, in effect 2024
Ontario Proposed CPPA alignment May introduce provincial law

Quebec Law 25 Compliance

Quebec's Law 25 deserves special attention as it's significantly stricter than PIPEDA:

// Quebec Law 25 specific requirements
interface QuebecLaw25Config {
  organizationInfo: {
    name: string;
    address: string;
    privacyOfficer: ContactInfo;
  };
  piaRequired: boolean;
  biometricData: boolean;
  profiling: boolean;
  crossBorderTransfers: boolean;
}

class QuebecLaw25Manager {
  private config: QuebecLaw25Config;

  constructor(config: QuebecLaw25Config) {
    this.config = config;
  }

  // Privacy Impact Assessment requirement
  requiresPIA(): boolean {
    // Law 25 requires PIA for:
    // - Any acquisition, development, or redesign of IT system involving PI
    // - Biometric identification or verification systems
    // - Profiling activities
    return this.config.piaRequired ||
           this.config.biometricData ||
           this.config.profiling;
  }

  // Cross-border transfer assessment
  async assessCrossBorderTransfer(
    destination: string,
    dataCategories: string[]
  ): Promise<TransferAssessment> {
    // Law 25 requires assessment before transfers outside Quebec
    const assessment: TransferAssessment = {
      destination,
      dataCategories,
      riskLevel: 'medium',
      requirements: [],
      contractualClauses: [],
    };

    // Check if destination provides adequate protection
    const adequacyList = ['EU', 'UK', 'Switzerland', 'Japan', 'Israel'];
    const destinationCountry = this.extractCountry(destination);

    if (!adequacyList.includes(destinationCountry)) {
      assessment.requirements.push('Contractual safeguards required');
      assessment.requirements.push('Privacy impact assessment required');
      assessment.contractualClauses = this.generateContractualClauses();
    }

    // Sensitive data requires additional safeguards
    const sensitiveCategories = ['health', 'biometric', 'financial', 'minor'];
    if (dataCategories.some(c => sensitiveCategories.includes(c))) {
      assessment.riskLevel = 'high';
      assessment.requirements.push('Encryption in transit and at rest required');
      assessment.requirements.push('Access controls and audit logging required');
    }

    return assessment;
  }

  private extractCountry(destination: string): string {
    // Extract country from destination string
    return destination.split(',').pop()?.trim() || destination;
  }

  private generateContractualClauses(): string[] {
    return [
      'Recipient must provide equivalent level of protection',
      'Recipient must notify of any government access requests',
      'Data subject rights must be enforceable',
      'Audit rights for the Quebec organization',
      'Breach notification within 24 hours',
    ];
  }

  // Right to de-indexation (unique to Quebec)
  async handleDeindexationRequest(
    userId: string,
    searchEngines: string[]
  ): Promise<DeindexationResult> {
    // Law 25 grants right to have personal information de-indexed
    // from search engines in certain circumstances

    const results: SearchEngineResult[] = [];

    for (const engine of searchEngines) {
      try {
        const result = await this.submitDeindexationRequest(engine, userId);
        results.push(result);
      } catch (error) {
        results.push({
          searchEngine: engine,
          status: 'failed',
          error: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }

    return {
      requestDate: new Date(),
      userId,
      results,
      overallStatus: results.every(r => r.status === 'submitted') ? 'complete' : 'partial',
    };
  }

  private async submitDeindexationRequest(
    engine: string,
    userId: string
  ): Promise<SearchEngineResult> {
    // In production, this would submit to search engine APIs
    return {
      searchEngine: engine,
      status: 'submitted',
      referenceNumber: `${engine}-${Date.now()}`,
    };
  }
}

interface TransferAssessment {
  destination: string;
  dataCategories: string[];
  riskLevel: 'low' | 'medium' | 'high';
  requirements: string[];
  contractualClauses: string[];
}

interface DeindexationResult {
  requestDate: Date;
  userId: string;
  results: SearchEngineResult[];
  overallStatus: 'complete' | 'partial' | 'failed';
}

interface SearchEngineResult {
  searchEngine: string;
  status: 'submitted' | 'failed';
  referenceNumber?: string;
  error?: string;
}

Building a CPPA-Ready Consent Management Platform

Here's how to integrate these requirements into your CMP:

// Comprehensive Canadian privacy CMP configuration
interface CanadianCMPConfig {
  pipeadCompliance: boolean;
  cppaReadiness: boolean;
  quebecLaw25: boolean;
  provincialLaws: ('BC_PIPA' | 'AB_PIPA')[];
}

class CanadianPrivacyCMP {
  private config: CanadianCMPConfig;
  private pipedaManager: PIPEDAConsentManager;
  private cppaManager: CPPAComplianceManager;
  private quebecManager?: QuebecLaw25Manager;

  constructor(config: CanadianCMPConfig, complianceConfigs: ComplianceConfigs) {
    this.config = config;
    this.pipedaManager = new PIPEDAConsentManager(complianceConfigs.pipeda);
    this.cppaManager = new CPPAComplianceManager(complianceConfigs.cppa);

    if (config.quebecLaw25) {
      this.quebecManager = new QuebecLaw25Manager(complianceConfigs.quebec);
    }
  }

  async presentConsentUI(userLocation: UserLocation): Promise<ConsentUIConfig> {
    // Determine applicable laws based on location
    const applicableLaws = this.determineApplicableLaws(userLocation);

    // Build consent UI configuration
    const uiConfig: ConsentUIConfig = {
      showBanner: true,
      bannerStyle: this.determineBannerStyle(applicableLaws),
      purposes: this.buildPurposeList(applicableLaws),
      vendors: [], // Quebec requires vendor-level consent in some cases
      requireExplicitConsent: applicableLaws.includes('QUEBEC_LAW25'),
      showRejectButton: true, // CPPA requires equal prominence
      languageOptions: this.getLanguageOptions(userLocation),
    };

    // Quebec-specific requirements
    if (applicableLaws.includes('QUEBEC_LAW25')) {
      uiConfig.additionalDisclosures = [
        'Data may be transferred outside Quebec',
        'You have the right to request de-indexation',
        'Automated decision-making may be used',
      ];
    }

    return uiConfig;
  }

  private determineApplicableLaws(location: UserLocation): string[] {
    const laws: string[] = ['PIPEDA']; // Federal baseline

    // CPPA preparation
    if (this.config.cppaReadiness) {
      laws.push('CPPA_PREP');
    }

    // Provincial laws
    if (location.province === 'QC') {
      laws.push('QUEBEC_LAW25');
    }
    if (location.province === 'BC') {
      laws.push('BC_PIPA');
    }
    if (location.province === 'AB') {
      laws.push('AB_PIPA');
    }

    return laws;
  }

  private determineBannerStyle(laws: string[]): BannerStyle {
    // Quebec requires most prominent style
    if (laws.includes('QUEBEC_LAW25')) {
      return {
        type: 'modal',
        position: 'center',
        size: 'large',
        blockContent: true,
      };
    }

    // CPPA preparation suggests clear consent mechanisms
    if (laws.includes('CPPA_PREP')) {
      return {
        type: 'banner',
        position: 'bottom',
        size: 'medium',
        blockContent: false,
      };
    }

    return {
      type: 'banner',
      position: 'bottom',
      size: 'small',
      blockContent: false,
    };
  }

  private buildPurposeList(laws: string[]): ConsentPurpose[] {
    const purposes: ConsentPurpose[] = [
      {
        id: 'essential',
        name: 'Essential Cookies',
        description: 'Required for the website to function properly',
        required: true,
        defaultEnabled: true,
      },
      {
        id: 'analytics',
        name: 'Analytics',
        description: 'Help us understand how visitors use our website',
        required: false,
        defaultEnabled: false,
      },
      {
        id: 'marketing',
        name: 'Marketing',
        description: 'Used to deliver personalized advertisements',
        required: false,
        defaultEnabled: false,
      },
    ];

    // Quebec requires more granular purposes
    if (laws.includes('QUEBEC_LAW25')) {
      purposes.push({
        id: 'profiling',
        name: 'Profiling',
        description: 'Analysis of your preferences and behavior',
        required: false,
        defaultEnabled: false,
      });
    }

    return purposes;
  }

  private getLanguageOptions(location: UserLocation): string[] {
    // Quebec requires French option
    const languages = ['en'];
    if (location.province === 'QC' || location.preferredLanguage === 'fr') {
      languages.unshift('fr');
    }
    return languages;
  }
}

interface ComplianceConfigs {
  pipeda: PIPEDAConsentConfig;
  cppa: CPPAComplianceConfig;
  quebec: QuebecLaw25Config;
}

interface UserLocation {
  province: string;
  country: string;
  preferredLanguage?: string;
}

interface ConsentUIConfig {
  showBanner: boolean;
  bannerStyle: BannerStyle;
  purposes: ConsentPurpose[];
  vendors: string[];
  requireExplicitConsent: boolean;
  showRejectButton: boolean;
  languageOptions: string[];
  additionalDisclosures?: string[];
}

interface BannerStyle {
  type: 'banner' | 'modal';
  position: 'top' | 'bottom' | 'center';
  size: 'small' | 'medium' | 'large';
  blockContent: boolean;
}

interface ConsentPurpose {
  id: string;
  name: string;
  description: string;
  required: boolean;
  defaultEnabled: boolean;
}

What This Means for Your Business

Canadian privacy law is undergoing its most significant transformation in over two decades. The transition from PIPEDA's ombudsman model to the CPPA's enforcement-oriented approach—with penalties reaching 5% of global revenue—represents a fundamental shift in how organizations must think about privacy.

For businesses operating in Canada, the message is clear: treat Canadian personal information with the same rigor as EU data under GDPR. The CPPA's algorithmic transparency requirements, data portability rights, and enhanced consent provisions align closely with global privacy standards, and organizations that have already invested in GDPR compliance will find themselves well-positioned.

Key actions to take now include auditing your current consent mechanisms against the stricter CPPA standards, implementing algorithmic explainability for any automated decision systems, building data portability infrastructure, and paying special attention to Quebec's Law 25 if you have users in that province.

The window for preparation is now. Organizations that proactively align their privacy programs with CPPA requirements will not only avoid significant penalties but will also build the trust that increasingly privacy-aware Canadian consumers expect.

Vanliga frågor

Is PIPEDA the same as GDPR?
No. While similar in principles (consent, access), PIPEDA is currently less prescriptive and has lower penalties. However, the proposed CPPA (Bill C-27) will bring Canadian law much closer to the GDPR standard.
G

GetCookies-teamet

Skriven och underhållen av GetCookies-teamet på Getia AS i Oslo, Norge. Artikeln är allmän information om cookiesamtycke, inte juridisk rådgivning.

Skanna din egen webbplats gratis

Gratisplanen kör bannern på en webbplats med tre cookieskanningar i månaden. Inget kort, inget säljsamtal.