UNPKG

anon-identity

Version:

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

350 lines 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentEnabledServiceProvider = void 0; const service_provider_1 = require("./service-provider"); const scope_validator_1 = require("../agent/scope-validator"); 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"); class AgentEnabledServiceProvider 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(); // 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 }; } // Check revocation status if revocation service is available if (this.agentRevocationService && agentValidation.agentDID && agentValidation.parentDID) { const delegationCredential = presentation.verifiableCredential?.[0]; // 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 if (agentValidation.agentDID && agentValidation.parentDID && agentValidation.grantedScopes) { const sessionId = `${agentValidation.agentDID}:${Date.now()}`; this.agentSessions.set(sessionId, { agentDID: agentValidation.agentDID, parentDID: agentValidation.parentDID, scopes: agentValidation.grantedScopes, sessionId }); // Log successful authorization 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' } })); // Log session start await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.SESSION_START, { agentDID: agentValidation.agentDID, parentDID: agentValidation.parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.SUCCESS, scopes: agentValidation.grantedScopes, sessionId, details: { message: 'Agent session started' } })); } // Extract delegation credential for result const delegationCredential = presentation.verifiableCredential?.[0]; return { valid: true, holder: agentValidation.agentDID, credentials: [{ id: delegationCredential.id, issuer: delegationCredential.issuer, type: delegationCredential.type, attributes: { agentDID: agentValidation.agentDID, parentDID: agentValidation.parentDID, scopes: agentValidation.grantedScopes, agentName: delegationCredential.credentialSubject.name } }], timestamp }; } catch (error) { return { valid: false, errors: [ new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.PROCESSING_ERROR, error instanceof Error ? error.message : 'Unknown error during agent verification') ], timestamp }; } } async validateAgent(presentation, requiredScopes) { return this.scopeValidator.validateAgentPresentation(presentation, requiredScopes, this.serviceManifest.serviceDID); } hasScope(agentDID, scope) { // Check all sessions for this agent for (const session of this.agentSessions.values()) { if (session.agentDID === agentDID) { return session.scopes.includes(scope); } } return false; } getAgentScopes(agentDID) { // Collect all scopes from all sessions for this agent const allScopes = new Set(); for (const session of this.agentSessions.values()) { if (session.agentDID === agentDID) { session.scopes.forEach(scope => allScopes.add(scope)); } } return Array.from(allScopes); } getAgentParent(agentDID) { for (const session of this.agentSessions.values()) { if (session.agentDID === agentDID) { return session.parentDID; } } return null; } async revokeAgentSession(agentDID) { let revoked = false; for (const [sessionId, session] of this.agentSessions.entries()) { if (session.agentDID === agentDID) { // Log session end await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.SESSION_END, { agentDID: session.agentDID, parentDID: session.parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.SUCCESS, scopes: session.scopes, sessionId: session.sessionId, details: { message: 'Agent session revoked', reason: 'Manual revocation' } })); this.agentSessions.delete(sessionId); revoked = true; } } return revoked; } getAllAgentSessions() { const sessions = []; for (const [sessionId, session] of this.agentSessions.entries()) { sessions.push({ sessionId: session.sessionId, agentDID: session.agentDID, parentDID: session.parentDID, scopes: session.scopes }); } return sessions; } // Helper method to create a service manifest for this provider static createManifestBuilder(serviceDID, name) { return new service_manifest_1.ServiceManifestBuilder(serviceDID, name); } // Method to get human-readable scope descriptions getScopeDescriptions() { const descriptions = {}; const allScopes = [ ...this.serviceManifest.requiredScopes, ...(this.serviceManifest.optionalScopes || []) ]; allScopes.forEach(scope => { descriptions[scope.id] = scope.description; }); return descriptions; } // Method to check if a set of scopes meets minimum requirements validateScopeRequirements(grantedScopes) { const requiredScopeIds = this.serviceManifest.requiredScopes.map(s => s.id); const optionalScopeIds = (this.serviceManifest.optionalScopes || []).map(s => s.id); const missingRequired = requiredScopeIds.filter(s => !grantedScopes.includes(s)); const additionalOptional = grantedScopes.filter(s => optionalScopeIds.includes(s) && !requiredScopeIds.includes(s)); return { valid: missingRequired.length === 0, missingRequired, additionalOptional }; } // Log when an agent uses a specific scope async logScopeUsage(agentDID, scope, details) { // Find agent session let agentSession = null; for (const session of this.agentSessions.values()) { if (session.agentDID === agentDID) { agentSession = session; break; } } if (!agentSession) { throw new Error('No active session for agent'); } // Check if agent has the scope if (!agentSession.scopes.includes(scope)) { await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.SCOPE_USAGE, { agentDID, parentDID: agentSession.parentDID, serviceDID: this.serviceManifest.serviceDID, status: types_1.ActivityStatus.DENIED, scopes: [scope], sessionId: agentSession.sessionId, details: { ...details, errorMessage: 'Agent does not have required scope', scopesGranted: agentSession.scopes } })); throw new Error(`Agent does not have scope: ${scope}`); } // Log successful scope usage await this.activityLogger.logActivity((0, activity_logger_1.createActivity)(types_1.ActivityType.SCOPE_USAGE, { agentDID, parentDID: agentSession.parentDID, serviceDID: this.serviceManifest.serviceDID, status: details?.success === false ? types_1.ActivityStatus.FAILED : types_1.ActivityStatus.SUCCESS, scopes: [scope], sessionId: agentSession.sessionId, details: { ...details, message: `Agent used scope: ${scope}` } })); } // Get the activity logger instance getActivityLogger() { return this.activityLogger; } } exports.AgentEnabledServiceProvider = AgentEnabledServiceProvider; //# sourceMappingURL=service-provider-agent.js.map