UNPKG

anon-identity

Version:

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

337 lines 17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentEnabledServiceProviderV2 = void 0; const service_provider_1 = require("./service-provider"); const scope_validator_1 = require("../agent/scope-validator"); const scope_registry_1 = require("../agent/scope-registry"); const service_manifest_1 = require("../agent/service-manifest"); const verification_errors_1 = require("./verification-errors"); const activity_logger_1 = require("../agent/activity/activity-logger"); const types_1 = require("../agent/activity/types"); const delegation_chain_validator_1 = require("../agent/delegation-chain-validator"); class AgentEnabledServiceProviderV2 extends service_provider_1.ServiceProvider { constructor(name, did, trustedIssuers = [], options = {}) { super(name, trustedIssuers, options); this.agentSessions = new Map(); this.scopeValidator = new scope_validator_1.ScopeValidator(); this.requireAgentValidation = options.requireAgentValidation ?? false; this.agentRevocationService = options.agentRevocationService; this.activityLogger = new activity_logger_1.ActivityLogger(); this.validateChains = options.validateDelegationChains ?? true; this.maxChainDepth = options.maxChainDepth ?? 5; // Initialize chain validator if provided if (options.agentIdentityManager && options.delegationManager) { this.chainValidator = new delegation_chain_validator_1.DelegationChainValidator(options.delegationManager, options.agentIdentityManager); } // Create default service manifest if not provided this.serviceManifest = options.serviceManifest || service_manifest_1.ServiceManifestBuilder.createBasicReadService(did, name); } setServiceManifest(manifest) { this.serviceManifest = manifest; } getServiceManifest() { return this.serviceManifest; } async verifyPresentation(presentation) { // Check if this is an agent presentation if (presentation.type?.includes('AgentPresentation')) { return this.verifyAgentPresentation(presentation); } // For regular presentations, use parent class verification const result = await super.verifyPresentation(presentation); // If agent validation is required and this is not an agent, fail if (this.requireAgentValidation && result.valid) { return { ...result, valid: false, errors: [ ...(result.errors || []), new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.INVALID_CREDENTIAL, 'Agent validation required but presentation is not from an agent') ] }; } return result; } async verifyAgentPresentation(presentation) { const errors = []; const timestamp = new Date(); const startTime = Date.now(); // Extract agent info early for logging const delegationCredential = presentation.verifiableCredential?.[0]; const agentDID = delegationCredential?.credentialSubject?.id || 'unknown'; const parentDID = delegationCredential?.credentialSubject?.parentDID || 'unknown'; try { // Log authentication attempt await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.AUTHENTICATION, { agentDID, parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.SUCCESS, scopes: [], details: { presentationId: presentation.id, message: 'Agent authentication attempt started' } })); // First, perform standard presentation verification const baseResult = await super.verifyPresentation(presentation); if (!baseResult.valid) { // Log authentication failure await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.AUTHENTICATION, { agentDID, parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.FAILED, scopes: [], details: { errorMessage: 'Base presentation verification failed', errors: baseResult.errors?.map(e => e.message) } })); return baseResult; } // Extract required scopes from manifest const requiredScopes = this.serviceManifest.requiredScopes.map(s => s.id); // Validate agent authorization const agentValidation = await this.validateAgent(presentation, requiredScopes); if (!agentValidation.isValid) { // Log authorization failure await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.AUTHORIZATION, { agentDID, parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.DENIED, scopes: requiredScopes, details: { errorMessage: 'Agent authorization failed', errors: agentValidation.errors, scopesRequested: requiredScopes, scopesDenied: requiredScopes } })); return { valid: false, errors: agentValidation.errors?.map(e => new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.INSUFFICIENT_PERMISSIONS, e)) || [], timestamp }; } // NEW: Validate delegation chain if enabled and chain validator is available if (this.validateChains && this.chainValidator && agentValidation.agentDID) { const rootDID = await this.findRootDID(delegationCredential); if (rootDID) { const chainValidation = await this.chainValidator.validateDelegationChain(agentValidation.agentDID, rootDID, this.serviceManifest.serviceDID); if (!chainValidation.valid) { await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.AUTHORIZATION, { agentDID, parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.DENIED, scopes: requiredScopes, details: { errorMessage: 'Delegation chain validation failed', errors: chainValidation.errors, warnings: chainValidation.warnings } })); return { valid: false, errors: chainValidation.errors.map(e => new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.INVALID_CREDENTIAL, e)), timestamp }; } // Store chain information in session if valid if (chainValidation.chain && agentValidation.parentDID && agentValidation.grantedScopes) { const sessionId = `${agentValidation.agentDID}:${Date.now()}`; this.agentSessions.set(sessionId, { agentDID: agentValidation.agentDID, parentDID: agentValidation.parentDID, scopes: agentValidation.grantedScopes, sessionId, delegationChain: this.chainValidator.exportChain(chainValidation.chain) }); // Log successful authorization with chain info await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.AUTHORIZATION, { agentDID: agentValidation.agentDID, parentDID: agentValidation.parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.SUCCESS, scopes: agentValidation.grantedScopes, sessionId, details: { scopesRequested: requiredScopes, scopesGranted: agentValidation.grantedScopes, message: 'Agent authorization successful with chain validation', chainDepth: chainValidation.chain.currentDepth, chainWarnings: chainValidation.warnings } })); return { valid: true, credentials: baseResult.credentials, timestamp, holder: agentValidation.agentDID }; } } } // Check revocation status if revocation service is available if (this.agentRevocationService && agentValidation.agentDID && agentValidation.parentDID) { // Check if agent is revoked const isRevoked = await this.agentRevocationService.isAgentRevoked(agentValidation.agentDID, agentValidation.parentDID); if (isRevoked) { return { valid: false, errors: [new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.CREDENTIAL_REVOKED, 'Agent has been revoked')], timestamp }; } // Check service-specific revocation const isServiceRevoked = await this.agentRevocationService.isAgentServiceRevoked(agentValidation.agentDID, agentValidation.parentDID, this.serviceManifest.serviceDID); if (isServiceRevoked) { return { valid: false, errors: [new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.CREDENTIAL_REVOKED, `Agent access to this service has been revoked`)], timestamp }; } // Validate the delegation credential itself const credentialValidation = await this.agentRevocationService.validateDelegationCredential(delegationCredential); if (!credentialValidation.valid) { return { valid: false, errors: [new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.CREDENTIAL_REVOKED, credentialValidation.reason || 'Delegation credential validation failed')], timestamp }; } } // Store agent session (without chain info if chain validation wasn't performed) if (agentValidation.agentDID && agentValidation.parentDID && agentValidation.grantedScopes) { const sessionId = `${agentValidation.agentDID}:${Date.now()}`; if (!this.agentSessions.has(sessionId)) { this.agentSessions.set(sessionId, { agentDID: agentValidation.agentDID, parentDID: agentValidation.parentDID, scopes: agentValidation.grantedScopes, sessionId }); } return { valid: true, credentials: baseResult.credentials, timestamp, holder: agentValidation.agentDID }; } return { valid: true, credentials: baseResult.credentials, timestamp }; } catch (error) { // Log error await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.AUTHENTICATION, { agentDID, parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.FAILED, scopes: [], details: { errorMessage: error instanceof Error ? error.message : 'Unknown error', errorType: 'exception' } })); return { valid: false, errors: [new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.INVALID_CREDENTIAL, `Agent verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`)], timestamp }; } } async validateAgent(presentation, requiredScopes) { // Extract delegation credential const delegationCredential = presentation.verifiableCredential?.[0]; if (!delegationCredential) { return { isValid: false, errors: ['No delegation credential found in presentation'] }; } // Extract agent information const agentDID = delegationCredential.credentialSubject.id; const parentDID = delegationCredential.credentialSubject.parentDID; // Get service-specific grants const serviceGrants = delegationCredential.credentialSubject.services[this.serviceManifest.serviceDID]; if (!serviceGrants) { return { isValid: false, errors: [`No access grants found for service ${this.serviceManifest.serviceDID}`], agentDID, parentDID }; } // Validate required scopes const grantedScopes = serviceGrants.scopes; const missingScopes = requiredScopes.filter(scope => !grantedScopes.includes(scope)); if (missingScopes.length > 0) { return { isValid: false, errors: [`Missing required scopes: ${missingScopes.join(', ')}`], agentDID, parentDID, grantedScopes }; } // Additional scope validation const scopeRegistry = scope_registry_1.ScopeRegistry.getInstance(); const validation = scopeRegistry.validateScopes(requiredScopes); if (!validation.valid) { return { isValid: false, errors: validation.errors || ['Invalid scope configuration'], agentDID, parentDID, grantedScopes }; } return { isValid: true, agentDID, parentDID, grantedScopes }; } async findRootDID(credential) { // For now, we'll check if the issuer is in trusted issuers // In a full implementation, this would traverse up the chain if (this.isTrustedIssuer(credential.issuer)) { return credential.issuer; } // Check if any trusted issuer is in the credential subject's parent chain // This is simplified - in reality would need to traverse the full chain if (this.isTrustedIssuer(credential.credentialSubject.parentDID)) { return credential.credentialSubject.parentDID; } return null; } isTrustedIssuer(did) { // Access parent class method or implement our own check return this.trustedIssuers?.has(did) || false; } getAgentSession(sessionId) { return this.agentSessions.get(sessionId); } clearExpiredSessions() { // Clear sessions older than 1 hour const oneHourAgo = Date.now() - (60 * 60 * 1000); for (const [sessionId, session] of this.agentSessions.entries()) { const sessionTime = parseInt(sessionId.split(':')[1]); if (sessionTime < oneHourAgo) { this.agentSessions.delete(sessionId); } } } } exports.AgentEnabledServiceProviderV2 = AgentEnabledServiceProviderV2; //# sourceMappingURL=service-provider-agent-v2.js.map