Ejemplo n.º 1
0
        /// <summary>
        /// Retrieves the secret from the cache, if not present calls the underlying <see cref="ISecretRepository"/> to acquire and caches the result.
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        /// <remarks>We don't do any locking internally, it's a case of last one wins but all that would happen is that the cache duration would be increased slightly.</remarks>
        public string ClientSecret(string clientId)
        {
            var key     = CacheRegion.CacheKey(clientId);
            var options = new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = duration
            };

            return(cache.GetOrCreate(key, () => repository.ClientSecret(clientId), options));
        }
        /// <summary>
        /// Retrieves the secret from the cache, if not present calls the underlying <see cref="ISecretRepository"/> to acquire and caches the result.
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        /// <remarks>We don't do any locking internally, it's a case of last one wins but all that would happen is that the cache duration would be increased slightly.</remarks>
        public string ClientSecret(string clientId)
        {
            var key = CacheRegion.CacheKey(clientId);

            return(cache.GetOrCreateAtomic(key, entry =>
            {
                entry.AbsoluteExpirationRelativeToNow = duration;
                return repository.ClientSecret(clientId);
            }));
        }
        /// <summary>
        /// Retrieves the secret from the cache, if not present calls the underlying <see cref="ISecretRepository"/> to acquire and caches the result.
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        /// <remarks>We don't do any locking internally, it's a case of last one wins but all that would happen is that the cache duration would be increased slightly.</remarks>
        public string ClientSecret(string clientId)
        {
            var secret = Get(clientId);

            if (secret == null)
            {
                secret = repository.ClientSecret(clientId);
                if (!string.IsNullOrEmpty(secret))
                {
                    Cache(clientId, secret, DateTimeOffset.UtcNow.Add(duration));
                }
            }

            return(secret);
        }
        /// <summary>
        /// Sends the message after adding the HMAC signature if a HMAC client id header is present.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // Only try and sign if we have a client id
            var clientId = request.Headers.GetValues <string>(HmacAuthentication.ClientIdHeader).FirstOrDefault();

            if (string.IsNullOrEmpty(clientId))
            {
                return(base.SendAsync(request, cancellationToken));
            }

            // Can we sign it
            var secret = secretRepository.ClientSecret(clientId);

            if (secret == null)
            {
                Logger.WarnFormat("No secret for client id {0}: {1}", clientId, request.RequestUri);
            }
            else
            {
                // Need the date present in UTC for the signature calculation
                request.Headers.Date = DateTimeOffset.UtcNow;

                // Get the canonical representation.
                var representation = representationBuilder.BuildRequestRepresentation(request);

                // Compute the signature
                var signature = signatureCalculator.Signature(secret, representation, scheme);

                if (string.IsNullOrEmpty(signature))
                {
                    Logger.WarnFormat("Invalid signature for client id {0}: {1}", clientId, request.RequestUri);
                }
                else
                {
                    // Valid signature so add the authentication header
                    Logger.InfoFormat("HMAC signed for client id {0}: {1}", clientId, request.RequestUri);
                    var header = new AuthenticationHeaderValue(HmacAuthentication.AuthenticationSchemePrefix + scheme, signature);
                    request.Headers.Authorization = header;
                }
            }

            return(base.SendAsync(request, cancellationToken));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Retrieves the secret from the cache, if not present calls the underlying <see cref="ISecretRepository"/> to acquire and caches the result.
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public string ClientSecret(string clientId)
        {
            var key = CacheRegion.CacheKey(clientId);

            return(cachePolicy.Cache(key, () => Task.FromResult(repository.ClientSecret(clientId))).GetAwaiter().GetResult());
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Validates whether we are using HMAC and if so is the signature valid.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task <bool> IsValid(HttpRequestMessage request)
        {
            // TODO: Generalize so we can using any HMAC scheme.
            if (request.Headers.Authorization == null || request.Headers.Authorization.Scheme != HmacAuthentication.AuthenticationScheme)
            {
                // No authorization or not our authorization schema, so no
                Logger.DebugFormat("Not our authorization schema: {0}", request.RequestUri);
                return(false);
            }

            var isDateValid = request.IsMessageDateValid(ClockDrift);

            if (!isDateValid)
            {
                // Date is not present or valid
                Logger.DebugFormat("Invalid date: {0}", request.RequestUri);
                return(false);
            }

            var userName = request.Headers.GetValues <string>(HmacAuthentication.ClientIdHeader).FirstOrDefault();

            if (string.IsNullOrEmpty(userName))
            {
                // No user name
                Logger.DebugFormat("No client id: {0}", request.RequestUri);
                return(false);
            }

            var secret = secretRepository.ClientSecret(userName);

            if (secret == null)
            {
                // Can't find a secret for the user, so no
                Logger.DebugFormat("No secret for client id {0}: {1}", userName, request.RequestUri);
                return(false);
            }

            if (!await request.Content.IsMd5Valid())
            {
                // MD5 is invalid, so no
                Logger.DebugFormat("Invalid MD5 hash: {0}", request.RequestUri);
                return(false);
            }

            // Construct the representation
            var representation = representationBuilder.BuildRequestRepresentation(request);

            if (representation == null)
            {
                // Something broken in the representation, so no
                Logger.DebugFormat("Invalid canonical representation: {0}", request.RequestUri);
                return(false);
            }

            // Compute the signature
            // TODO: Pass the encryption algorithm used e.g. SHA256
            var signature = signatureCalculator.Signature(secret, representation);

            // Have we seen it before
            if (objectCache.Contains(signature))
            {
                // Already seen, so no to avoid replay attack
                Logger.WarnFormat("Request replayed {0}: {1}", signature, request.RequestUri);
                return(false);
            }

            // Validate the signature
            var result = request.Headers.Authorization.Parameter == signature;

            if (result)
            {
                // Store valid signatures to avoid replay attack
                objectCache.Set(signature, userName, DateTimeOffset.UtcNow.AddMinutes(ValidityPeriod), CacheRegion);
            }

            // Return the signature validation.
            return(result);
        }
        /// <summary>
        /// Validates whether we are using HMAC and if so is the signature valid.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        /// <remarks>
        /// We log all failures a Info, except for replay attacks which are logged as Warn as they may be a symptom of an attack.
        /// </remarks>
        public async Task <bool> IsValid(HttpRequestMessage request)
        {
            if (request.Headers.Authorization == null)
            {
                // No authorization or not our authorization schema, so no
                Logger.InfoFormat("No authorization: {0}", request.RequestUri);
                return(false);
            }

            if (!request.Headers.Authorization.Scheme.StartsWith(HmacAuthentication.AuthenticationSchemePrefix))
            {
                // No authorization or not our authorization schema, so no
                Logger.InfoFormat("Not our authorization schema: {0}", request.RequestUri);
                return(false);
            }

            var isDateValid = request.IsMessageDateValid(ClockDrift);

            if (!isDateValid)
            {
                // Date is not present or valid
                Logger.InfoFormat("Invalid date: {0}", request.RequestUri);
                return(false);
            }

            var userName = request.Headers.GetValues <string>(HmacAuthentication.ClientIdHeader).FirstOrDefault();

            if (string.IsNullOrEmpty(userName))
            {
                // No user name
                Logger.InfoFormat("No client id: {0}", request.RequestUri);
                return(false);
            }

            var secret = secretRepository.ClientSecret(userName);

            if (secret == null)
            {
                // Can't find a secret for the user, so no
                Logger.InfoFormat("No secret for client id {0}: {1}", userName, request.RequestUri);
                return(false);
            }

            var contentValid = await request.Content.IsMd5Valid().ConfigureAwait(false);

            if (!contentValid)
            {
                // MD5 is invalid, so no
                Logger.InfoFormat("Invalid MD5 hash: {0}", request.RequestUri);
                return(false);
            }

            // Construct the representation
            var representation = representationBuilder.BuildRequestRepresentation(request);

            if (representation == null)
            {
                // Something broken in the representation, so no
                Logger.InfoFormat("Invalid canonical representation: {0}", request.RequestUri);
                return(false);
            }

            // Compute the signature
            var scheme    = request.Headers.Authorization.Scheme.Substring(HmacAuthentication.AuthenticationSchemePrefix.Length);
            var signature = signatureCalculator.Signature(secret, representation, scheme);

            // Have we seen it before
            if (cache.Contains(signature))
            {
                // Already seen, so no to avoid replay attack
                Logger.WarnFormat("Request replayed {0}: {1}", signature, request.RequestUri);
                return(false);
            }

            // Validate the signature
            var result = request.Headers.Authorization.Parameter == signature;

            if (!result)
            {
                // Signatures differ, so no
                Logger.InfoFormat("Signatures differ {0}: {1}", signature, request.RequestUri);
            }
            else
            {
                // Store valid signatures to avoid replay attack
                cache.Add(signature, DateTimeOffset.UtcNow.AddMinutes(ValidityPeriod));
            }

            // Return the signature validation.
            return(result);
        }