/// <summary> /// Logs an ignored signature validation. /// </summary> /// <param name="logger">The <see cref="ILogger"/>.</param> /// <param name="result">The <see cref="SignatureValidationResult"/>.</param> /// <param name="ex">The <see cref="Exception"/>, if any.</param> public static void SignatureValidationIgnored(this ILogger logger, SignatureValidationResult result, Exception ex = null) { _signatureValidationIgnored( logger, result?.Status ?? SignatureValidationResultStatus.Disabled, ex); }
/// <summary> /// Logs a successful signature validation. /// </summary> /// <param name="logger">The <see cref="ILogger"/>.</param> /// <param name="result">The <see cref="SignatureValidationResult"/>.</param> /// <param name="ex">The <see cref="Exception"/>, if any.</param> public static void SignatureValidationSucceeded(this ILogger logger, SignatureValidationResult result, Exception ex = null) { _signatureValidationSucceeded( logger, result?.Status ?? SignatureValidationResultStatus.OK, result?.ClientId, ex); }
/// <summary> /// Logs a failed signature validation. /// </summary> /// <param name="logger">The <see cref="ILogger"/>.</param> /// <param name="result">The <see cref="SignatureValidationResult"/>.</param> /// <param name="ex">The <see cref="Exception"/>, if any.</param> public static void SignatureValidationFailed(this ILogger logger, SignatureValidationResult result, Exception ex = null) { _signatureValidationFailed( logger, result?.Status ?? SignatureValidationResultStatus.InternalError, result?.ServerTimestamp ?? 0, result?.SignatureValue, result?.ClientId, result?.ComputedSignature, result?.SignatureBodySource == null ? null : Convert.ToBase64String(result.SignatureBodySource), ex); }
/// <inheritdoc /> public async Task <SignatureValidationResult> Validate(HttpRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } var serverTimestamp = _clock.UtcNow.ToUnixTimeSeconds(); SignatureValidationResult result; if (_options.Disabled) { result = new SignatureValidationResult( SignatureValidationResultStatus.Disabled, serverTimestamp); _logger.SignatureValidationIgnored(result); return(result); } var headerValue = request.Headers[_options.HeaderName]; if (string.IsNullOrWhiteSpace(headerValue)) { result = new SignatureValidationResult( SignatureValidationResultStatus.HeaderNotFound, serverTimestamp); _logger.SignatureValidationFailed(result); return(result); } var signatureValue = headerValue.ToString().Trim(); var headerMatch = _signaturePatternParser.Match(signatureValue); if (!headerMatch.Success) { result = new SignatureValidationResult( SignatureValidationResultStatus.HeaderParseError, serverTimestamp, signatureValue: signatureValue); _logger.SignatureValidationFailed(result); return(result); } var signatureComponents = new SignatureValueComponents { ClientId = headerMatch.Groups["ClientId"]?.Value, Nonce = headerMatch.Groups["Nonce"]?.Value, Timestamp = long.Parse(headerMatch.Groups["Timestamp"]?.Value ?? "0", CultureInfo.InvariantCulture), SignatureBody = headerMatch.Groups["SignatureBody"]?.Value, }; var clientOptions = _options.Clients.FirstOrDefault(x => string.Equals(x.ClientId, signatureComponents.ClientId, StringComparison.Ordinal)); if (clientOptions == null) { result = new SignatureValidationResult( SignatureValidationResultStatus.ClientIdNotFound, serverTimestamp, signatureValue: signatureValue, clientId: signatureComponents.ClientId); _logger.SignatureValidationFailed(result); return(result); } if (await _nonceRepository.Exists(signatureComponents.Nonce)) { result = new SignatureValidationResult( SignatureValidationResultStatus.NonceHasBeenUsedBefore, serverTimestamp, signatureValue: signatureValue, clientId: clientOptions.ClientId); _logger.SignatureValidationFailed(result); return(result); } if (Math.Abs(serverTimestamp - signatureComponents.Timestamp) > _options.ClockSkew.TotalSeconds) { result = new SignatureValidationResult( SignatureValidationResultStatus.TimestampIsOff, serverTimestamp, signatureValue: signatureValue, clientId: clientOptions.ClientId); _logger.SignatureValidationFailed(result); return(result); } var signatureBodySourceComponents = clientOptions.SignatureBodySourceComponents.Any() ? clientOptions.SignatureBodySourceComponents : DefaultConstants.SignatureBodySourceComponents; byte[] body = null; if (signatureBodySourceComponents.Contains(SignatureBodySourceComponents.Body)) { #if NETCOREAPP2_1 request.EnableRewind(); #endif #if NETCOREAPP3_0 request.EnableBuffering(); #endif request.Body.Seek(0, SeekOrigin.Begin); using (var memoryStream = _memoryStreamManager.GetStream()) { await request.Body.CopyToAsync(memoryStream); body = memoryStream.ToArray(); } request.Body.Seek(0, SeekOrigin.Begin); } var signatureBodySourceParameters = new SignatureBodySourceParameters( request.Method, new Uri($"{request.Scheme}://{request.Host}{request.Path}{request.QueryString}"), request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()), signatureComponents.Nonce, signatureComponents.Timestamp, clientOptions.ClientId, signatureBodySourceComponents, body); var signatureBodySource = await _signatureBodySourceBuilder.Build(signatureBodySourceParameters); var signatureBodyParameters = new SignatureBodyParameters(signatureBodySource, clientOptions.ClientSecret); var signature = await _signatureBodySigner.Sign(signatureBodyParameters); if (!string.Equals(signature, signatureComponents.SignatureBody, StringComparison.Ordinal)) { result = new SignatureValidationResult( SignatureValidationResultStatus.SignatureDoesntMatch, serverTimestamp, signatureValue: signatureValue, clientId: clientOptions.ClientId, computedSignature: signature, signatureBodySource: signatureBodySource); _logger.SignatureValidationFailed(result); return(result); } await _nonceRepository.Add(signatureComponents.Nonce); result = new SignatureValidationResult( SignatureValidationResultStatus.OK, serverTimestamp, signatureValue: signatureValue, clientId: clientOptions.ClientId, computedSignature: signature); _logger.SignatureValidationSucceeded(result); return(result); }
/// <summary> /// Initializes a new instance of the <see cref="RequestsSignatureValidationException"/> class with a specified <see cref="SignatureValidationResult"/>. /// </summary> /// <param name="result">The <see cref="SignatureValidationResult"/>.</param> public RequestsSignatureValidationException(SignatureValidationResult result) : base(result?.Status.ToString()) { Result = result; }