/// <summary>
        /// Finds the access token for the specified user in the cache or via the refresh token by renewing it.
        /// </summary>
        /// <param name="userId">The id of the user</param>
        /// <returns>Either the access token if successful or default if something went wrong
        /// (The user may need to login again if the refresh token in the key vault was invalid.)</returns>
        public async Task <string?> GetAccessTokenAsync(string userId)
        {
            try
            {
                //
                // Get the refresh token
                //
                var oldRefreshToken = _secretManager.GetSecret(userId);

                // No refresh token found -> User needs to login first
                if (oldRefreshToken is null)
                {
                    return(default);
        /// <summary>
        /// Validates the signature of the slack request.
        /// </summary>
        /// <param name="body">The request body</param>
        /// <param name="headers">The request headers</param>
        /// <returns>True if the signature is valid</returns>
        private bool IsValidSignature(string body, IHeaderDictionary headers)
        {
            var timestamp     = headers["X-Slack-Request-Timestamp"];
            var signature     = headers["X-Slack-Signature"];
            var signingSecret = _secretManager.GetSecret("Slack-SigningSecret");

            var encoding = new UTF8Encoding();

            using var hmac = new HMACSHA256(encoding.GetBytes(signingSecret));
            var hash         = hmac.ComputeHash(encoding.GetBytes($"v0:{timestamp}:{body}"));
            var ownSignature = $"v0={BitConverter.ToString(hash).Replace("-", "", StringComparison.CurrentCulture).ToLower()}";

            return(ownSignature.Equals(signature, StringComparison.CurrentCulture));
        }
        public CommandController(
            IConfiguration configuration,
            IHttpClientFactory factory,
            IDataProtectionProvider provider,
            ISecretManager secretManager,
            ICosmosManager cosmosManager,
            ITokenManager tokenManager,
            ITCManager dataManager
            )
        {
#pragma warning disable CA2208 // Instantiate argument exceptions correctly
            if (provider is null)
            {
                var message = "IDataProtectionProvider";
                throw new ArgumentNullException(message);
            }

            if (factory is null)
            {
                throw new ArgumentNullException("IHttpClientFactory");
            }

            if (secretManager is null)
            {
                throw new ArgumentNullException("ISecretManager");
            }
#pragma warning restore CA2208 // Instantiate argument exceptions correctly

            _configuration = configuration;
            _protector     = provider.CreateProtector("UUIDProtector");
            _secretManager = secretManager;
            _cosmosManager = cosmosManager;
            _httpClient    = factory.CreateClient("BotClient");
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _secretManager.GetSecret("Slack-SlackbotOAuthAccessToken"));
            _tokenManager  = tokenManager;
            _tcDataManager = dataManager;

            _commandHandler = new CommandHandler(_protector, _cosmosManager, _secretManager, _tokenManager, _tcDataManager);
        }