Skip to content

Commit

Permalink
Fix request scheme behind proxy (#11)
Browse files Browse the repository at this point in the history
* add optional using of X-Forwarded-Proto header when app is behind proxy

* add missing tests

* add short desc of new config option, style changes (review)

Co-authored-by: Krzysztof Majk <[email protected]>
  • Loading branch information
funcmike and Krzysztof Majk authored Mar 28, 2022
1 parent b8b72b6 commit 4e8b5f4
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
40 changes: 40 additions & 0 deletions softaware.Authentication.Hmac.AspNetCore.Test/MiddlewareTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,36 @@ public async Task Request_WithDefaultAuthorizationProvider_Authorized()
Assert.True(response.StatusCode == HttpStatusCode.OK);
}
}

[Fact]
public async Task Request_Authorized_WithTrustProxy()
{
using (var client = this.GetHttpClientWithTrustProxyOption(
new Dictionary<string, string>() {{"appId", "MNpx/353+rW+pqv8UbRTAtO1yoabl8/RFDAv/615u5w="}},
"appId",
"MNpx/353+rW+pqv8UbRTAtO1yoabl8/RFDAv/615u5w="))
{
client.DefaultRequestHeaders.Add("X-Forwarded-Proto", "http");

var response = await client.GetAsync("api/test");
Assert.True(response.StatusCode == HttpStatusCode.OK);
}
}

[Fact]
public async Task Request_Unauthorized_WithTrustProxy()
{
using (var client = this.GetHttpClientWithTrustProxyOption(
new Dictionary<string, string>() {{"appId", "MNpx/353+rW+pqv8UbRTAtO1yoabl8/RFDAv/615u5w="}},
"appId",
"MNpx/353+rW+pqv8UbRTAtO1yoabl8/RFDAv/615u5w="))
{
client.DefaultRequestHeaders.Add("X-Forwarded-Proto", "https");

var response = await client.GetAsync("api/test");
Assert.True(response.StatusCode == HttpStatusCode.Unauthorized);
}
}

[Theory]
[InlineData("appId", "YXJld3JzZHJkc2FhcndlZQ==")]
Expand Down Expand Up @@ -152,5 +182,15 @@ private HttpClient GetHttpClientWithDefaultAuthorizationProvider(IDictionary<str
var factory = new TestWebApplicationFactoryWithDefaultAuthorizationProvider(hmacAuthenticatedApps);
return factory.CreateDefaultClient(new ApiKeyDelegatingHandler(appId, apiKey));
}

private HttpClient GetHttpClientWithTrustProxyOption(IDictionary<string, string> hmacAuthenticatedApps, string appId, string apiKey)
{
var factory = new TestWebApplicationFactory(o =>
{
o.AuthorizationProvider = new MemoryHmacAuthenticationProvider(hmacAuthenticatedApps);
o.TrustProxy = true;
});
return factory.CreateDefaultClient(new ApiKeyDelegatingHandler(appId, apiKey));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public class HmacAuthenticationSchemeOptions : AuthenticationSchemeOptions

public string AuthenticationScheme { get; set; }

/// <summary>
/// If <see langword="true"/>, the request scheme from the 'X-Forwarded-Proto' header is used to validate the request.
/// </summary>
public bool TrustProxy { get; set; }

private IDictionary<string, string> hmacAuthenticatedApps = new Dictionary<string, string>();

[Obsolete("Please use the MemoryHmacAuthenticationProvider for configuring the HMAC apps in-memory. This property will be removed in future versions of this package.", error: false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private async Task<bool> IsValidRequestAsync(HttpRequest req, byte[] body, strin
{
var requestContentBase64String = string.Empty;
var absoluteUri = string.Concat(
req.Scheme,
this.GetRequestScheme(req),
"://",
req.Host.ToUriComponent(),
req.PathBase.ToUriComponent(),
Expand Down Expand Up @@ -178,6 +178,15 @@ private bool IsReplayRequest(string nonce, string requestTimeStamp)
return false;
}

private string GetRequestScheme(HttpRequest req)
{
if (this.Options.TrustProxy && req.Headers.TryGetValue("X-Forwarded-Proto", out var scheme))
{
return scheme;
}
return req.Scheme;
}

private static byte[] ComputeHash(byte[] body)
{
using (var md5 = MD5.Create())
Expand Down

0 comments on commit 4e8b5f4

Please sign in to comment.