/// <summary> /// Process an offer, taking previous proposals into account. The framework doesn't fully /// support credential proposals, and I don't know how to override one service function /// so for now we just use a custom handler and this function. /// </summary> /// <param name="agentContext"></param> /// <param name="credentialOffer"></param> /// <param name="connection"></param> /// <returns></returns> private async Task <string> ProcessOfferAsync(IAgentContext agentContext, CredentialOfferMessage credentialOffer, ConnectionRecord connection) { var offerAttachment = credentialOffer.Offers.FirstOrDefault(x => x.Id == "libindy-cred-offer-0") ?? throw new ArgumentNullException(nameof(CredentialOfferMessage.Offers)); var offerJson = offerAttachment.Data.Base64.GetBytesFromBase64().GetUTF8String(); var offer = JObject.Parse(offerJson); var definitionId = offer["cred_def_id"].ToObject <string>(); var schemaId = offer["schema_id"].ToObject <string>(); // check if credential already exists CredentialRecord credentialRecord; try { credentialRecord = await _credentialService.GetByThreadIdAsync(agentContext, credentialOffer.GetThreadId()); if (credentialRecord.ConnectionId != connection.Id) { throw new AriesFrameworkException(ErrorCode.InvalidRecordData, "Connection from credential offer is not same as previously stored record."); } } catch { // Write offer record to local wallet credentialRecord = new CredentialRecord { Id = Guid.NewGuid().ToString(), ConnectionId = connection?.Id, }; credentialRecord.SetTag(TagConstants.Role, TagConstants.Holder); credentialRecord.SetTag(TagConstants.LastThreadId, credentialOffer.GetThreadId()); await _recordService.AddAsync(agentContext.Wallet, credentialRecord); var THCredentialExchange = new TestHarnessCredentialExchange { RecordId = credentialRecord.Id, ThreadId = credentialOffer.GetThreadId(), State = TestHarnessCredentialExchangeState.OfferReceived }; _credentialCache.Set(THCredentialExchange.ThreadId, THCredentialExchange); } credentialRecord.OfferJson = offerJson; credentialRecord.CredentialDefinitionId = definitionId; credentialRecord.SchemaId = schemaId; credentialRecord.CredentialAttributesValues = credentialOffer.CredentialPreview?.Attributes .Select(x => new CredentialPreviewAttribute { Name = x.Name, MimeType = x.MimeType, Value = x.Value }).ToArray(); credentialRecord.State = CredentialState.Offered; await _recordService.UpdateAsync(agentContext.Wallet, credentialRecord); _eventAggregator.Publish(new ServiceMessageProcessingEvent { RecordId = credentialRecord.Id, MessageType = credentialOffer.Type, ThreadId = credentialOffer.GetThreadId() }); return(credentialRecord.Id); }
public async Task <IActionResult> SendCredentialOfferAsync(OperationBody body) { var context = await _agentContextProvider.GetContextAsync(); var issuer = await _provisionService.GetProvisioningAsync(context.Wallet); var threadId = body.Id; await Task.Delay(5000); string connectionId; TestHarnessCredentialExchange THCredentialExchange; CredentialOfferMessage credentialOffer; CredentialRecord credentialRecord; // Send offer in response to proposal if (threadId != null) { THCredentialExchange = _credentialCache.Get <TestHarnessCredentialExchange>(threadId); credentialRecord = await _credentialService.GetAsync(context, THCredentialExchange.RecordId); connectionId = credentialRecord.ConnectionId; var offerJson = await AnonCreds.IssuerCreateCredentialOfferAsync(context.Wallet, credentialRecord.CredentialDefinitionId); var offerJobj = JObject.Parse(offerJson); var schemaId = offerJobj["schema_id"].ToObject <string>(); // Update credential record credentialRecord.SchemaId = schemaId; credentialRecord.OfferJson = offerJson; credentialRecord.State = CredentialState.Offered; credentialRecord.SetTag(TagConstants.IssuerDid, issuer.IssuerDid); await _recordService.UpdateAsync(context.Wallet, credentialRecord); credentialOffer = new CredentialOfferMessage { CredentialPreview = new CredentialPreviewMessage { Attributes = CleanCredentialPreviewAttributes(credentialRecord.CredentialAttributesValues.ToArray()) }, Comment = "credential-offer", // ACA-Py requires comment Offers = new Attachment[] { new Attachment { Id = "libindy-cred-offer-0", MimeType = CredentialMimeTypes.ApplicationJsonMimeType, Data = new AttachmentContent { Base64 = offerJson.GetUTF8Bytes().ToBase64String() } } } }; credentialOffer.ThreadFrom(threadId); THCredentialExchange.State = TestHarnessCredentialExchangeState.OfferSent; } // Send Offer to start credential issuance flow else { var offer = body.Data; connectionId = (string)offer["connection_id"]; var credentialPreview = offer["credential_preview"].ToObject <CustomCredentialPreviewMessage>(); var credentialDefinitionId = (string)offer["cred_def_id"]; credentialPreview.Attributes = CleanCredentialPreviewAttributes(credentialPreview.Attributes); (credentialOffer, credentialRecord) = await _credentialService.CreateOfferAsync(context, new OfferConfiguration { CredentialAttributeValues = credentialPreview.Attributes, CredentialDefinitionId = credentialDefinitionId, IssuerDid = issuer.IssuerDid }, connectionId); THCredentialExchange = new TestHarnessCredentialExchange { RecordId = credentialRecord.Id, ThreadId = credentialRecord.GetTag(TagConstants.LastThreadId), State = TestHarnessCredentialExchangeState.OfferSent }; _credentialCache.Set(THCredentialExchange.ThreadId, THCredentialExchange); } var connection = await _connectionService.GetAsync(context, connectionId); // TODO: Handle AriesFrameworkException if connection not found // TODO: find better way to listen for changes. As the state can move to other statest than just RequestReceived // Listen for credential request to update the state UpdateStateOnMessage(THCredentialExchange, TestHarnessCredentialExchangeState.RequestReceived, _ => _.MessageType == MessageTypes.IssueCredentialNames.RequestCredential && _.ThreadId == THCredentialExchange.ThreadId); await _messageService.SendAsync(context.Wallet, credentialOffer, connection); return(Ok(THCredentialExchange)); }
/// <inheritdoc /> public virtual async Task <string> ProcessOfferAsync(IAgentContext agentContext, CredentialOfferMessage credentialOffer, ConnectionRecord connection) { var offerJson = credentialOffer.OfferJson; var offer = JObject.Parse(offerJson); var definitionId = offer["cred_def_id"].ToObject <string>(); var schemaId = offer["schema_id"].ToObject <string>(); // Write offer record to local wallet var credentialRecord = new CredentialRecord { Id = Guid.NewGuid().ToString(), OfferJson = offerJson, ConnectionId = connection.Id, CredentialDefinitionId = definitionId, CredentialAttributesValues = credentialOffer.Preview?.Attributes, SchemaId = schemaId, State = CredentialState.Offered }; credentialRecord.SetTag(TagConstants.Role, TagConstants.Holder); credentialRecord.SetTag(TagConstants.LastThreadId, credentialOffer.GetThreadId()); await RecordService.AddAsync(agentContext.Wallet, credentialRecord); EventAggregator.Publish(new ServiceMessageProcessingEvent { RecordId = credentialRecord.Id, MessageType = credentialOffer.Type, ThreadId = credentialOffer.GetThreadId() }); return(credentialRecord.Id); }