anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
252 lines • 10.1 kB
JavaScript
;
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