本文へ移動
cccskills
無料GitHub で公開

consent-receipt-spec

Implement the Kantara Initiative consent receipt specification including machine-readable receipt structure, JWT-based verification mechanisms, receipt lifecycle management, and integration patterns for consent management platforms. Supports ISO/IEC 27560 consent record information structure.

インストール方法を見る

含まれるファイル(5)

  • SKILL.md17.0 KB
  • assets/template.md964 B
  • references/standards.md821 B
  • references/workflows.md1.1 KB
  • scripts/process.py8.7 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Kantara Initiative Consent Receipt Specification

Overview

A consent receipt is a record of a consent transaction provided to the individual (data subject) as evidence that consent was given. The Kantara Initiative Consent Receipt Specification v1.1 defines a standard, machine-readable format that enables individuals to track and manage their consent across multiple organizations. This skill covers the implementation of consent receipts aligned with the Kantara specification and ISO/IEC 27560:2023.

Consent Receipt Structure

Core Fields (Kantara v1.1)

FieldTypeRequiredDescription
versionStringYesSpecification version (e.g., "KI-CR-v1.1.0")
jurisdictionStringYesLegal jurisdiction (ISO 3166-1 alpha-2)
consentTimestampDateTimeYesUTC timestamp of consent grant
collectionMethodStringYesHow consent was collected (web form, verbal, paper)
consentReceiptIDUUIDYesUnique identifier for this receipt
publicKeyStringNoPublic key for receipt verification
languageStringYesLanguage of the consent interaction (BCP 47)
piiPrincipalIdStringYesIdentifier for the data subject
piiControllersArrayYesList of data controllers
policyUrlURLYesLink to the privacy policy
servicesArrayYesServices for which consent is given
sensitiveBooleanYesWhether special category data is involved
spiCatArrayNoSpecial categories of data processed

PII Controller Object

FieldTypeRequiredDescription
piiControllerStringYesName of the controller organization
onBehalfBooleanNoWhether acting on behalf of another controller
contactStringYesContact information for the controller
addressObjectYesPhysical address of the controller
emailStringYesContact email address
phoneStringNoContact phone number
piiControllerUrlURLNoURL of the controller's website

Service Object

FieldTypeRequiredDescription
serviceStringYesName of the service
purposesArrayYesList of processing purposes

Purpose Object

FieldTypeRequiredDescription
purposeStringYesDescription of the processing purpose
purposeCategoryArrayYesCategory codes for the purpose
consentTypeStringYes"explicit" or "implicit"
piiCategoryArrayYesCategories of PII processed
primaryPurposeBooleanYesWhether this is the primary purpose
terminationStringYesHow consent can be withdrawn
thirdPartyDisclosureBooleanYesWhether data is shared with third parties
thirdPartyNameStringNoName of third party (if applicable)

Machine-Readable Receipt Format

JSON Consent Receipt

{
  "version": "KI-CR-v1.1.0",
  "jurisdiction": "EU",
  "consentTimestamp": "2026-03-14T10:30:00.000Z",
  "collectionMethod": "web_form",
  "consentReceiptID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "language": "en",
  "piiPrincipalId": "user-98765",
  "piiControllers": [
    {
      "piiController": "Cipher Engineering Labs",
      "onBehalf": false,
      "contact": "Data Protection Officer",
      "address": {
        "streetAddress": "100 Technology Drive",
        "locality": "London",
        "region": "Greater London",
        "postalCode": "EC2A 1NT",
        "country": "GB"
      },
      "email": "dpo@cipherengineeringlabs.com",
      "phone": "+44-20-7946-0958",
      "piiControllerUrl": "https://www.cipherengineeringlabs.com"
    }
  ],
  "policyUrl": "https://www.cipherengineeringlabs.com/privacy-policy",
  "services": [
    {
      "service": "Privacy Analytics Platform",
      "purposes": [
        {
          "purpose": "Provide personalized privacy compliance recommendations",
          "purposeCategory": ["core_service"],
          "consentType": "explicit",
          "piiCategory": ["contact_information", "professional_information"],
          "primaryPurpose": true,
          "termination": "Account settings > Privacy > Withdraw consent",
          "thirdPartyDisclosure": false
        },
        {
          "purpose": "Send product updates and feature announcements",
          "purposeCategory": ["marketing"],
          "consentType": "explicit",
          "piiCategory": ["contact_information"],
          "primaryPurpose": false,
          "termination": "Unsubscribe link in email or Account settings",
          "thirdPartyDisclosure": false
        },
        {
          "purpose": "Aggregate usage analytics to improve platform features",
          "purposeCategory": ["analytics"],
          "consentType": "explicit",
          "piiCategory": ["usage_data", "device_information"],
          "primaryPurpose": false,
          "termination": "Account settings > Privacy > Analytics opt-out",
          "thirdPartyDisclosure": true,
          "thirdPartyName": "Cipher Analytics Processing Ltd"
        }
      ]
    }
  ],
  "sensitive": false,
  "spiCat": []
}

JWT-Based Verification Mechanism

Signed Consent Receipt Implementation

"""
Consent receipt generation and verification using JWT (JSON Web Tokens).
Implements Kantara Initiative Consent Receipt Specification v1.1
with cryptographic verification.
"""

import json
import uuid
from datetime import datetime, timezone
from typing import Optional
import jwt  # PyJWT
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend


class ConsentReceiptIssuer:
    """
    Issue and sign consent receipts as JWTs.
    Uses RS256 (RSA with SHA-256) for signing.
    """

    def __init__(self, private_key_pem: bytes, issuer_name: str):
        """
        Args:
            private_key_pem: PEM-encoded RSA private key
            issuer_name: Name of the issuing organization
        """
        self.private_key = serialization.load_pem_private_key(
            private_key_pem, password=None, backend=default_backend()
        )
        self.public_key = self.private_key.public_key()
        self.issuer_name = issuer_name

    def issue_receipt(
        self,
        principal_id: str,
        services: list[dict],
        jurisdiction: str,
        collection_method: str,
        policy_url: str,
        sensitive: bool = False,
        language: str = "en",
        controller_info: dict = None
    ) -> tuple[str, str]:
        """
        Generate a signed consent receipt.

        Args:
            principal_id: Data subject identifier
            services: List of service/purpose objects
            jurisdiction: Legal jurisdiction code
            collection_method: How consent was collected
            policy_url: URL of the privacy policy
            sensitive: Whether special categories are involved
            language: Language of consent interaction
            controller_info: PII controller details

        Returns:
            Tuple of (receipt_id, signed_jwt_string)
        """
        receipt_id = str(uuid.uuid4())

        if controller_info is None:
            controller_info = {
                "piiController": self.issuer_name,
                "onBehalf": False,
                "contact": "Data Protection Officer",
                "email": f"dpo@{self.issuer_name.lower().replace(' ', '')}.com"
            }

        receipt_payload = {
            "version": "KI-CR-v1.1.0",
            "jurisdiction": jurisdiction,
            "consentTimestamp": datetime.now(timezone.utc).isoformat(),
            "collectionMethod": collection_method,
            "consentReceiptID": receipt_id,
            "language": language,
            "piiPrincipalId": principal_id,
            "piiControllers": [controller_info],
            "policyUrl": policy_url,
            "services": services,
            "sensitive": sensitive,
            "spiCat": [],
            # JWT standard claims
            "iss": self.issuer_name,
            "sub": principal_id,
            "iat": int(datetime.now(timezone.utc).timestamp()),
            "jti": receipt_id,
        }

        # Sign the receipt as a JWT
        signed_token = jwt.encode(
            receipt_payload,
            self.private_key,
            algorithm="RS256",
            headers={"kid": f"{self.issuer_name}-consent-key-1"}
        )

        return receipt_id, signed_token

    def get_public_key_pem(self) -> str:
        """Export the public key for verification by data subjects."""
        return self.public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
        ).decode("utf-8")


class ConsentReceiptVerifier:
    """
    Verify signed consent receipts.
    Used by data subjects or auditors to confirm receipt authenticity.
    """

    def __init__(self):
        self.trusted_keys: dict[str, bytes] = {}

    def register_issuer_key(self, issuer_name: str, public_key_pem: str):
        """Register a trusted issuer's public key."""
        self.trusted_keys[issuer_name] = public_key_pem.encode("utf-8")

    def verify_receipt(self, token: str) -> tuple[bool, Optional[dict], Optional[str]]:
        """
        Verify a signed consent receipt JWT.

        Returns:
            Tuple of (is_valid, decoded_payload_or_None, error_message_or_None)
        """
        try:
            # Decode without verification first to get the issuer
            unverified = jwt.decode(token, options={"verify_signature": False})
            issuer = unverified.get("iss")

            if issuer not in self.trusted_keys:
                return (False, None, f"Unknown issuer: {issuer}")

            public_key = serialization.load_pem_public_key(
                self.trusted_keys[issuer],
                backend=default_backend()
            )

            # Verify the signature
            decoded = jwt.decode(
                token,
                public_key,
                algorithms=["RS256"],
                issuer=issuer
            )

            # Validate required Kantara fields
            required_fields = [
                "version", "jurisdiction", "consentTimestamp",
                "collectionMethod", "consentReceiptID", "piiPrincipalId",
                "piiControllers", "policyUrl", "services", "sensitive"
            ]

            missing = [f for f in required_fields if f not in decoded]
            if missing:
                return (False, decoded, f"Missing required fields: {missing}")

            return (True, decoded, None)

        except jwt.ExpiredSignatureError:
            return (False, None, "Receipt JWT has expired")
        except jwt.InvalidSignatureError:
            return (False, None, "Invalid signature — receipt may have been tampered with")
        except jwt.DecodeError as e:
            return (False, None, f"Failed to decode JWT: {str(e)}")

Receipt Lifecycle Management

Lifecycle States

ISSUED --> ACTIVE --> WITHDRAWN
  |          |           |
  |          v           v
  |       UPDATED    ARCHIVED
  |          |
  v          v
EXPIRED   ACTIVE (new version)

Lifecycle Manager

"""
Manage the lifecycle of consent receipts including
updates, withdrawals, and expiration tracking.
"""

from dataclasses import dataclass
from datetime import datetime, timezone
from enum import Enum


class ReceiptStatus(Enum):
    ISSUED = "issued"
    ACTIVE = "active"
    UPDATED = "updated"
    WITHDRAWN = "withdrawn"
    EXPIRED = "expired"
    ARCHIVED = "archived"


@dataclass
class ReceiptRecord:
    receipt_id: str
    principal_id: str
    status: ReceiptStatus
    issued_at: datetime
    last_updated: datetime
    withdrawn_at: datetime | None
    jwt_token: str
    version: int
    superseded_by: str | None
    purposes: list[str]


class ConsentReceiptLifecycle:
    """
    Manage consent receipt state transitions and history.
    """

    def __init__(self, receipt_store, issuer: ConsentReceiptIssuer):
        self.store = receipt_store
        self.issuer = issuer

    def activate_receipt(self, receipt_id: str) -> bool:
        """Transition a receipt from ISSUED to ACTIVE."""
        record = self.store.get(receipt_id)
        if record and record.status == ReceiptStatus.ISSUED:
            record.status = ReceiptStatus.ACTIVE
            record.last_updated = datetime.now(timezone.utc)
            self.store.update(record)
            return True
        return False

    def update_receipt(
        self,
        receipt_id: str,
        updated_services: list[dict],
        jurisdiction: str,
        policy_url: str
    ) -> str | None:
        """
        Update an existing receipt by issuing a new version.
        The old receipt is marked as UPDATED and linked to the new one.

        Returns the new receipt ID or None if update failed.
        """
        old_record = self.store.get(receipt_id)
        if not old_record or old_record.status not in [
            ReceiptStatus.ACTIVE, ReceiptStatus.ISSUED
        ]:
            return None

        # Issue new receipt
        new_receipt_id, new_jwt = self.issuer.issue_receipt(
            principal_id=old_record.principal_id,
            services=updated_services,
            jurisdiction=jurisdiction,
            collection_method="consent_update",
            policy_url=policy_url
        )

        # Create new record
        new_record = ReceiptRecord(
            receipt_id=new_receipt_id,
            principal_id=old_record.principal_id,
            status=ReceiptStatus.ACTIVE,
            issued_at=datetime.now(timezone.utc),
            last_updated=datetime.now(timezone.utc),
            withdrawn_at=None,
            jwt_token=new_jwt,
            version=old_record.version + 1,
            superseded_by=None,
            purposes=[p["purpose"] for s in updated_services for p in s.get("purposes", [])]
        )
        self.store.save(new_record)

        # Mark old receipt as updated
        old_record.status = ReceiptStatus.UPDATED
        old_record.superseded_by = new_receipt_id
        old_record.last_updated = datetime.now(timezone.utc)
        self.store.update(old_record)

        return new_receipt_id

    def withdraw_consent(self, receipt_id: str, reason: str = "") -> bool:
        """
        Withdraw consent and mark the receipt accordingly.

        Returns True if withdrawal was successful.
        """
        record = self.store.get(receipt_id)
        if not record or record.status not in [
            ReceiptStatus.ACTIVE, ReceiptStatus.ISSUED
        ]:
            return False

        record.status = ReceiptStatus.WITHDRAWN
        record.withdrawn_at = datetime.now(timezone.utc)
        record.last_updated = datetime.now(timezone.utc)
        self.store.update(record)

        return True

    def get_active_receipts(self, principal_id: str) -> list[ReceiptRecord]:
        """Get all active consent receipts for a data subject."""
        return self.store.find_by_principal(
            principal_id, status=ReceiptStatus.ACTIVE
        )

    def get_receipt_history(self, principal_id: str) -> list[ReceiptRecord]:
        """Get the complete consent receipt history for a data subject."""
        return self.store.find_by_principal(principal_id)

ISO/IEC 27560:2023 Alignment

Kantara CR FieldISO 27560 EquivalentNotes
consentReceiptIDConsent Record IdentifierUnique identifier for the record
consentTimestampDate and time of consentWhen consent was collected
piiPrincipalIdPII Principal IdentifierData subject identifier
piiControllersPII ControllerOrganization processing data
services.purposesPurpose(s)Processing purposes
services.purposes.piiCategoryPII CategoriesTypes of personal data
sensitiveSensitive PII indicatorSpecial category flag
jurisdictionJurisdictionApplicable legal framework
collectionMethodMechanism of consentHow consent was obtained
policyUrlPrivacy notice referenceLink to privacy notice

References

  • Kantara Initiative Consent Receipt Specification v1.1.0 (2018)
  • ISO/IEC 27560:2023 — Privacy Technologies — Consent Record Information Structure
  • ISO/IEC 29184:2020 — Guidelines for Online Privacy Notices and Consent
  • RFC 7519 — JSON Web Token (JWT)
  • W3C Data Privacy Vocabularies and Controls Community Group
  • IEEE P7012 — Standard for Machine Readable Personal Privacy Terms

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Implements 42 CFR Part 2 protections for substance use disorder patient records. Covers written consent requirements stricter than HIPAA, re-disclosure prohibition, court order procedures, qualified service organization agreements, and 2024 amendments aligning Part 2 with HIPAA. Keywords: 42 CFR Part 2, substance use disorder, SUD records, re-disclosure, consent, Part 2 amendments.

日本語の概要は準備中です。原文の説明を表示しています。

mukul975/Privacy-Data-Protection-Skills3032026年3月17日 更新

Guides assessment of third-country adequacy decisions under GDPR Article 45 for international data transfers. Covers the current EC adequacy decisions list, adequacy assessment criteria, partial adequacy handling, and monitoring of adequacy decision reviews. Keywords: adequacy decision, Article 45, third country, adequate protection, EC adequacy list.

日本語の概要は準備中です。原文の説明を表示しています。

mukul975/Privacy-Data-Protection-Skills3032026年3月17日 更新

Implements age-gating mechanisms for online services to restrict access based on user age. Covers hard gates versus soft gates, neutral age prompts, re-verification triggers, circumvention prevention, and regulatory requirements under GDPR, COPPA, UK Online Safety Act, and DSA. Keywords: age gate, age restriction, neutral prompt, children, online services, access control.

日本語の概要は準備中です。原文の説明を表示しています。

mukul975/Privacy-Data-Protection-Skills3032026年3月17日 更新

Evaluates and implements age estimation and verification technologies for online services. Covers facial age estimation, digital ID verification, self-declaration with risk assessment, AI-based age estimation, and the accuracy versus privacy tradeoff. Includes ICO guidance and euCONSENT framework. Keywords: age verification, age estimation, facial analysis, digital ID, children, online safety.

日本語の概要は準備中です。原文の説明を表示しています。

mukul975/Privacy-Data-Protection-Skills3032026年3月17日 更新

Preparing EU AI Act compliance documentation for high-risk AI systems. Covers Annex III classification, technical documentation under Art. 11, conformity assessment, risk management systems, and CE marking requirements. Keywords: EU AI Act, high-risk AI, Annex III, conformity assessment, CE marking.

日本語の概要は準備中です。原文の説明を表示しています。

mukul975/Privacy-Data-Protection-Skills3032026年3月17日 更新

Implements GDPR Art. 22 automated decision-making and AI Act Art. 14 human oversight requirements for AI systems. Covers identification of solely automated decisions, meaningful human intervention design, logic explanation mechanisms, and contestation procedures. Keywords: Art. 22, automated decision, human oversight, AI Act, profiling, contestation.

日本語の概要は準備中です。原文の説明を表示しています。

mukul975/Privacy-Data-Protection-Skills3032026年3月17日 更新

mukul975 のスキルをすべて見る

このスキルの問題を報告する