Skip to content

Commit

Permalink
Allow validation of signed http requests claims if present (#1415)
Browse files Browse the repository at this point in the history
* Allow validation of signed http requests claims if present
  • Loading branch information
keegan-caruso committed Jun 25, 2020
1 parent de89a30 commit 5560e2f
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -551,33 +551,55 @@ protected virtual async Task<SecurityToken> ValidateSignedHttpRequestPayloadAsyn
if (!(signedHttpRequest is JsonWebToken jwtSignedHttpRequest))
throw LogHelper.LogExceptionMessage(new SignedHttpRequestValidationException(LogHelper.FormatInvariant(LogMessages.IDX23030, signedHttpRequest.GetType(), typeof(JsonWebToken), signedHttpRequest)));

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ReplayValidatorAsync != null)
var validationParameters = signedHttpRequestValidationContext.SignedHttpRequestValidationParameters;

if (validationParameters.ReplayValidatorAsync != null)
await signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ReplayValidatorAsync(jwtSignedHttpRequest, signedHttpRequestValidationContext, cancellationToken).ConfigureAwait(false);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateTs)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateTs, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.Ts), SignedHttpRequestClaimTypes.Ts))
ValidateTsClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateM)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateM, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.M), SignedHttpRequestClaimTypes.M))
ValidateMClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateU)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateU, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.U), SignedHttpRequestClaimTypes.U))
ValidateUClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateP)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateP, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.P), SignedHttpRequestClaimTypes.P))
ValidatePClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateQ)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateQ, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.Q), SignedHttpRequestClaimTypes.Q))
ValidateQClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateH)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateH, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.H), SignedHttpRequestClaimTypes.H))
ValidateHClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

if (signedHttpRequestValidationContext.SignedHttpRequestValidationParameters.ValidateB)
if (ShouldValidate(jwtSignedHttpRequest, validationParameters.ValidateB, ValidateIfPresent(validationParameters, SignedHttpRequestClaimTypes.B), SignedHttpRequestClaimTypes.B))
ValidateBClaim(jwtSignedHttpRequest, signedHttpRequestValidationContext);

return jwtSignedHttpRequest;
}

/// <summary>
/// Determine if validation of a claim should happen.
/// </summary>
/// <param name="jwtSignedHttpRequest">The request being considered to validate the claim on.</param>
/// <param name="validateClaim">Force validation to always occur.</param>
/// <param name="validateIfPresent">Validate if the claim is present.</param>
/// <param name="claimName">The name of the claim to validate.</param>
/// <returns>Whether the given claim should be validated.</returns>
internal virtual bool ShouldValidate(JsonWebToken jwtSignedHttpRequest, bool validateClaim, bool validateIfPresent, string claimName)
{
return validateClaim || (validateIfPresent && jwtSignedHttpRequest.TryGetClaim(claimName, out var claimValue) && claimValue != null);
}

private static bool ValidateIfPresent(SignedHttpRequestValidationParameters validationParameters, string claim)
{
return validationParameters.ValidatePresentClaims &&
validationParameters.ClaimsToValidateWhenPresent != null &&
validationParameters.ClaimsToValidateWhenPresent.Contains(claim);
}

/// <summary>
/// Validates the signature of the signed http request using <paramref name="popKey"/>.
/// </summary>
Expand Down Expand Up @@ -632,8 +654,10 @@ internal virtual async Task<SecurityKey> ValidateSignatureAsync(JsonWebToken sig
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateTs"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateTs"/> is set to <c>true</c> or a ts claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.Ts"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidateTsClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
if (!signedHttpRequest.TryGetPayloadValue(SignedHttpRequestClaimTypes.Ts, out long tsClaimValue))
Expand All @@ -653,8 +677,10 @@ internal virtual void ValidateTsClaim(JsonWebToken signedHttpRequest, SignedHttp
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateM"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateM"/> is set to <c>true</c> or a m claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.M"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidateMClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
var expectedHttpMethod = signedHttpRequestValidationContext.HttpRequestData.Method;
Expand All @@ -679,8 +705,10 @@ internal virtual void ValidateMClaim(JsonWebToken signedHttpRequest, SignedHttpR
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateU"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateU"/> is set to <c>true</c> or a u claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.U"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidateUClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
var httpRequestUri = signedHttpRequestValidationContext.HttpRequestData.Uri;
Expand Down Expand Up @@ -711,8 +739,10 @@ internal virtual void ValidateUClaim(JsonWebToken signedHttpRequest, SignedHttpR
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateP"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateP"/> is set to <c>true</c> or a p claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.P"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidatePClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
var httpRequestUri = signedHttpRequestValidationContext.HttpRequestData.Uri;
Expand All @@ -738,8 +768,10 @@ internal virtual void ValidatePClaim(JsonWebToken signedHttpRequest, SignedHttpR
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateQ"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateQ"/> is set to <c>true</c> or a q claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.Q"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidateQClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
var httpRequestUri = signedHttpRequestValidationContext.HttpRequestData.Uri;
Expand Down Expand Up @@ -810,8 +842,10 @@ internal virtual void ValidateQClaim(JsonWebToken signedHttpRequest, SignedHttpR
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateH"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateH"/> is set to <c>true</c> or a h claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.H"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidateHClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
if (!signedHttpRequest.TryGetPayloadValue(SignedHttpRequestClaimTypes.H, out JArray hClaim) || hClaim == null)
Expand Down Expand Up @@ -876,8 +910,10 @@ internal virtual void ValidateHClaim(JsonWebToken signedHttpRequest, SignedHttpR
/// <param name="signedHttpRequest">A SignedHttpRequest.</param>
/// <param name="signedHttpRequestValidationContext">A structure that wraps parameters needed for SignedHttpRequest validation.</param>
/// <remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateB"/> is set to <c>true</c>.
/// </remarks>
/// This method will be executed only if <see cref="SignedHttpRequestValidationParameters.ValidateB"/> is set to <c>true</c> or a b claim is present and
/// <see cref="SignedHttpRequestValidationParameters.ValidatePresentClaims"/> is set to true and <see cref="SignedHttpRequestClaimTypes.B"/> is in
/// <see cref="SignedHttpRequestValidationParameters.ClaimsToValidateWhenPresent"/>.
/// </remarks>
internal virtual void ValidateBClaim(JsonWebToken signedHttpRequest, SignedHttpRequestValidationContext signedHttpRequestValidationContext)
{
var httpRequestBody = signedHttpRequestValidationContext.HttpRequestData.Body;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ public class SignedHttpRequestValidationParameters
/// <remarks>https://tools.ietf.org/html/draft-ietf-oauth-signed-http-request-03#section-5.1</remarks>
public bool AcceptUnsignedHeaders { get; set; } = true;

/// <summary>
/// Gets or sets the claims to validate if present.
/// </summary>
/// <remarks>
/// Validation will only occur if <see cref="ValidatePresentClaims"/> is set to <c>true</c>.
/// </remarks>
public IEnumerable<string> ClaimsToValidateWhenPresent { get; set; } = new List<string>
{
SignedHttpRequestClaimTypes.M,
SignedHttpRequestClaimTypes.P
};

/// <summary>
/// Gets or sets a collection of <see cref="SecurityKey"/> used for the 'cnf' claim decryption.
/// </summary>
Expand Down Expand Up @@ -216,5 +228,13 @@ public TimeSpan SignedHttpRequestLifetime
/// </summary>
/// <remarks>https://tools.ietf.org/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
public bool ValidateB { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating whether claims in <see cref="ClaimsToValidateWhenPresent"/> should be validated if present.
/// </summary>
/// <remarks>
/// Allows for validation of a claim if present, even if the validation option for the claim is set to <c>false</c>.
/// </remarks>
public bool ValidatePresentClaims { get; set; } = false;
}
}
Loading

0 comments on commit 5560e2f

Please sign in to comment.