/// <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);
        }
Beispiel #2
0
        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);
        }