UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

252 lines 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IdentityProviderV2 = void 0; const uuid_1 = require("uuid"); const jose_1 = require("jose"); const vc2_1 = require("../types/vc2"); const crypto_1 = require("../core/crypto"); const credential_status_1 = require("../status/credential-status"); const schemas_1 = require("./schemas"); const identity_provider_1 = require("./identity-provider"); const proof_manager_1 = require("../core/proof-manager"); /** * Enhanced Identity Provider with W3C VC 2.0 support */ class IdentityProviderV2 extends identity_provider_1.IdentityProvider { constructor(keyPair, storageProvider) { super(keyPair, storageProvider); this.nextStatusIndex = 0; this.statusList = new credential_status_1.StatusList2021(); } static async create(storageProvider) { const keyPair = await crypto_1.CryptoService.generateKeyPair(); const provider = new IdentityProviderV2(keyPair, storageProvider); // Call parent initialization const baseProvider = await identity_provider_1.IdentityProvider.create(storageProvider); Object.assign(provider, baseProvider); return provider; } /** * Issue a W3C VC 2.0 compliant credential */ async issueVerifiableCredentialV2(userDID, attributes, options = {}) { // Validate attributes against schema const validation = (0, schemas_1.validateAttributes)(attributes, schemas_1.BASIC_PROFILE_SCHEMA); if (!validation.valid) { throw new Error(`Invalid attributes: ${validation.errors.join(', ')}`); } // Auto-calculate isOver18 if dateOfBirth is provided if (attributes.dateOfBirth && !attributes.hasOwnProperty('isOver18')) { const birthDate = new Date(attributes.dateOfBirth); const today = new Date(); const age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { attributes.isOver18 = age - 1 >= 18; } else { attributes.isOver18 = age >= 18; } } const credentialId = `urn:uuid:${(0, uuid_1.v4)()}`; const now = new Date().toISOString(); // Build contexts const contexts = [ options.useV2Format !== false ? vc2_1.VC_V2_CONTEXTS.CREDENTIALS_V2 : schemas_1.CREDENTIAL_CONTEXTS.W3C_VC, schemas_1.CREDENTIAL_CONTEXTS.BASIC_PROFILE, vc2_1.VC_V2_CONTEXTS.ED25519_2020 ]; if (options.credentialStatus) { contexts.push(vc2_1.VC_V2_CONTEXTS.STATUS_LIST_2021); } if (options.termsOfUse) { contexts.push(vc2_1.VC_V2_CONTEXTS.TERMS_OF_USE); } if (options.additionalContexts) { contexts.push(...options.additionalContexts); } // Create the credential const credential = { "@context": contexts, id: credentialId, type: [ schemas_1.CREDENTIAL_TYPES.VERIFIABLE_CREDENTIAL, schemas_1.CREDENTIAL_TYPES.BASIC_PROFILE ], issuer: this.getDID(), validFrom: options.validFrom || now, credentialSubject: { id: userDID, ...attributes } }; // Add optional fields if (options.validUntil) { credential.validUntil = options.validUntil; } // Add credential status if requested if (options.credentialStatus) { credential.credentialStatus = await this.createCredentialStatus(credentialId, options.credentialStatus); } // Add terms of use if provided if (options.termsOfUse) { credential.termsOfUse = options.termsOfUse; } // Add evidence if provided if (options.evidence) { credential.evidence = options.evidence; } // For backward compatibility with VC 1.1 if (options.useV2Format === false) { credential.issuanceDate = credential.validFrom; if (credential.validUntil) { credential.expirationDate = credential.validUntil; } } // Sign the credential let signedCredential = await this.signCredentialV2(credential); // Add any additional proofs if (options.additionalProofs && options.additionalProofs.length > 0) { for (const proof of options.additionalProofs) { signedCredential = proof_manager_1.ProofManager.addProof(signedCredential, proof); } } // Store the issued credential await this.storageProvider.storeCredential(signedCredential); return signedCredential; } /** * Create credential status information */ async createCredentialStatus(credentialId, statusConfig) { if (!statusConfig) { throw new Error('Status configuration required'); } let statusListUrl = statusConfig.statusListUrl; let statusListIndex = statusConfig.statusListIndex; // If no URL provided, generate one if (!statusListUrl) { statusListUrl = `https://example.com/status/${this.getDID()}/list`; this.statusListUrl = statusListUrl; } // If no index provided, use next available if (statusListIndex === undefined) { statusListIndex = this.nextStatusIndex++; } switch (statusConfig.type) { case vc2_1.CredentialStatusType.STATUS_LIST_2021: return { id: `${statusListUrl}#${statusListIndex}`, type: vc2_1.CredentialStatusType.STATUS_LIST_2021, statusPurpose: 'revocation', statusListIndex, statusListCredential: statusListUrl }; case vc2_1.CredentialStatusType.REVOCATION_LIST_2020: return { id: statusListUrl, type: vc2_1.CredentialStatusType.REVOCATION_LIST_2020, revocationListIndex: statusListIndex.toString(), revocationListCredential: statusListUrl }; default: throw new Error(`Unsupported credential status type: ${statusConfig.type}`); } } /** * Sign a VC 2.0 credential */ async signCredentialV2(credential) { // Create a copy without the proof field for signing const credentialToSign = { ...credential }; delete credentialToSign.proof; // Convert private key to JWK format for jose const privateKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: Buffer.from(this.keyPair.publicKey).toString('base64url'), d: Buffer.from(this.keyPair.privateKey).toString('base64url') }; const privateKey = await (0, jose_1.importJWK)(privateKeyJwk, 'EdDSA'); // Create JWT const jwt = await new jose_1.SignJWT({ vc: credentialToSign }) .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: `${this.getDID()}#key-1` }) .setIssuedAt() .setIssuer(this.getDID()) .setSubject(Array.isArray(credential.credentialSubject) ? credential.credentialSubject[0].id || this.getDID() : credential.credentialSubject.id || this.getDID()) .sign(privateKey); // Add proof to credential const signedCredential = { ...credential, proof: { type: 'Ed25519Signature2020', created: new Date().toISOString(), proofPurpose: vc2_1.ProofPurpose.ASSERTION_METHOD, verificationMethod: `${this.getDID()}#key-1`, jws: jwt } }; return signedCredential; } /** * Revoke a credential using StatusList2021 */ async revokeCredentialV2(credentialId, statusListIndex) { this.statusList.setStatus(statusListIndex, true); // Also update the legacy revocation list for compatibility super.revokeCredential(credentialId); // Publish updated status list if (this.statusListUrl) { await this.publishStatusList(); } } /** * Publish the current status list */ async publishStatusList() { if (!this.statusListUrl) { this.statusListUrl = `https://example.com/status/${this.getDID()}/list`; } const statusListCredential = await this.statusList.createStatusListCredential({ id: this.getDID(), publicKey: this.keyPair.publicKey }, this.keyPair.privateKey, this.statusListUrl); // Store the status list credential await this.storageProvider.storeCredential(statusListCredential); return this.statusListUrl; } /** * Create example terms of use */ static createExampleTermsOfUse() { return { type: "IssuerPolicy", id: "https://example.com/policies/credential-tos", profile: "https://example.com/profiles/v1", prohibition: [{ assigner: "https://example.com/issuers/14", assignee: "AllVerifiers", target: "https://example.com/credentials/14", action: ["Archival"] }] }; } /** * Create example evidence */ static createExampleEvidence(verifierId) { return { type: ["DocumentVerification"], verifier: verifierId, evidenceDocument: "DriversLicense", subjectPresence: "Physical", documentPresence: "Physical", licenseNumber: "123-456-789" }; } } exports.IdentityProviderV2 = IdentityProviderV2; //# sourceMappingURL=identity-provider-v2.js.map