/// <summary>
        /// Process an incoming serialized message.
        ///
        /// The message will be deserialized, its signature verified,
        /// and it will be routed.
        ///
        /// If the message is a command to execute, the execution will
        /// run and the periodicUpdateFunction will be run regularly to
        /// update the caller on the runtime status.
        ///
        /// If the message is a status update, the periodicUpdateFunction
        /// will be executed only once.
        /// </summary>
        /// <param name="serializedMessage">Serialized Agent message</param>
        /// <param name="periodicUpdateFunction">Function which is invoked periodically to communicate runtime status</param>
        /// <returns></returns>
        public async Task ProcessMessageAsync(
            string serializedMessage,
            Func <ProviderWorkflowActionCollection, Task> periodicUpdateFunction)
        {
            var envelope = JsonConvert.DeserializeObject <AgentMessageEnvelope>(serializedMessage);

            envelope.MessageObject = null;

            if (envelope.Target != _options.Value.InstanceId)
            {
                return;
            }

            if (!await envelope.VerifyAndUnpack(_cryptographicImplementation))
            {
                await _eventDispatcher.DispatchEvent(
                    EventSinks.AuthJanitorSystemEvents.AnomalousEventOccurred,
                    nameof(AuthJanitorService.ProcessMessageAsync),
                    "Failed to verify agent message! This may indicate agent compromise.");

                throw new Exception("Message verification failed!");
            }

            if (envelope.MessageObject is AgentProviderCommandMessage)
            {
                var message = envelope.MessageObject as AgentProviderCommandMessage;
                await ExecuteAsync(message.ValidPeriod,
                                   periodicUpdateFunction,
                                   message.Providers.ToArray());
            }
            if (envelope.MessageObject is AgentProviderStatusMessage)
            {
                var message = envelope.MessageObject as AgentProviderStatusMessage;
                var taskId  = Guid.Parse(message.State);
                await periodicUpdateFunction(message.WorkflowActionCollection);
            }
        }
        public async Task <AccessTokenCredential> GetTokenCredentialAsync(Guid taskId, CancellationToken cancellationToken)
        {
            var task = await _rekeyingTasks.GetOne(taskId, cancellationToken);

            // Retrieve credentials for Task
            AccessTokenCredential credential = null;

            try
            {
                if (task.ConfirmationType == TaskConfirmationStrategies.AdminCachesSignOff)
                {
                    if (task.PersistedCredentialId == default)
                    {
                        throw new KeyNotFoundException("Cached sign-off is preferred but no credentials were persisted!");
                    }

                    if (_secureStorageProvider == null)
                    {
                        throw new NotSupportedException("Must register an ISecureStorageProvider");
                    }

                    credential = await _secureStorageProvider.Retrieve <AccessTokenCredential>(task.PersistedCredentialId);
                }
                else if (task.ConfirmationType == TaskConfirmationStrategies.AdminSignsOffJustInTime)
                {
                    credential = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync();
                }
                else if (task.ConfirmationType.UsesServicePrincipal())
                {
                    credential = await _identityService.GetAccessTokenForApplicationAsync();
                }
                else
                {
                    throw new NotSupportedException("No Access Tokens could be generated for this Task!");
                }

                if (credential == null || string.IsNullOrEmpty(credential.AccessToken))
                {
                    throw new InvalidOperationException("Access Token was found, but was blank or invalid");
                }

                credential.DisplayUserName = credential.Username;
                credential.DisplayEmail    = credential.Username;
                if (task.ConfirmationType.UsesOBOTokens())
                {
                    if (!string.IsNullOrEmpty(task.PersistedCredentialUser))
                    {
                        credential.DisplayUserName = task.PersistedCredentialUser;
                    }
                    else
                    {
                        credential.DisplayUserName = _identityService.UserName;
                        credential.DisplayEmail    = _identityService.UserEmail;
                    }
                }

                return(credential);
            }
            catch (Exception ex)
            {
                await _eventDispatcherService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task);

                throw ex;
            }
        }