From ad2e683e3a9b8dbb4194cc73c91d3a05805d7007 Mon Sep 17 00:00:00 2001 From: Ivan Josipovic <9521987+IvanJosipovic@users.noreply.github.com> Date: Sat, 25 Nov 2023 20:21:15 -0800 Subject: [PATCH] wip --- src/oidc-guard/Constants.cs | 14 +++----- src/oidc-guard/Program.cs | 31 ++++++++-------- tests/oidc-guard-tests/AuthTests.cs | 35 ++++++++++++++++++- .../EndToEnd/oidc-guard-values.yaml | 2 +- 4 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/oidc-guard/Constants.cs b/src/oidc-guard/Constants.cs index 96d6306..a65bbb4 100644 --- a/src/oidc-guard/Constants.cs +++ b/src/oidc-guard/Constants.cs @@ -2,17 +2,13 @@ public static class CustomHeaderNames { - public static readonly string XOriginalMethod = "X-Original-Method"; - - public static readonly string XOriginalUrl = "X-Original-Url"; - - public static readonly string XForwardedProto = "X-Forwarded-Proto"; - public static readonly string XForwardedHost = "X-Forwarded-Host"; - - public static readonly string XForwardedUri = "X-Forwarded-Uri"; - public static readonly string XForwardedMethod = "X-Forwarded-Method"; + public static readonly string XForwardedProto = "X-Forwarded-Proto"; + public static readonly string XForwardedUri = "X-Forwarded-Uri"; + public static readonly string XOriginalMethod = "X-Original-Method"; + public static readonly string XOriginalProto = "X-Original-Proto"; + public static readonly string XOriginalUrl = "X-Original-Url"; } public static class QueryParameters diff --git a/src/oidc-guard/Program.cs b/src/oidc-guard/Program.cs index 0d3a8f4..c50ebc8 100644 --- a/src/oidc-guard/Program.cs +++ b/src/oidc-guard/Program.cs @@ -82,7 +82,7 @@ public static void Main(string[] args) { string? authorization = context.Request.Headers.Authorization; - return settings.JWT.Enable && !string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ") + return settings.JWT.Enable && !string.IsNullOrEmpty(authorization) && authorization.StartsWith(JwtBearerDefaults.AuthenticationScheme + ' ') ? JwtBearerDefaults.AuthenticationScheme : settings.Cookie.Enable ? CookieAuthenticationDefaults.AuthenticationScheme @@ -213,15 +213,17 @@ public static void Main(string[] args) context.Request.Headers.Authorization = JwtBearerDefaults.AuthenticationScheme + ' ' + context.Request.Headers.Authorization; } - if (settings.JWT.EnableAccessTokenInQueryParameter && - context.Request.Path.StartsWithSegments("/auth") && - context.Request.Headers.TryGetValue(CustomHeaderNames.XOriginalUrl, out var originalUrlHeader) && - Uri.TryCreate(originalUrlHeader, UriKind.RelativeOrAbsolute, out var uri)) + if (settings.JWT.EnableAccessTokenInQueryParameter && context.Request.Path.StartsWithSegments("/auth")) { - if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(QueryParameters.AccessToken, out var token) && - !context.Request.Headers.ContainsKey(HeaderNames.Authorization)) + var originalUrl = GetOriginalUrl(context); + + if (originalUrl != null && Uri.TryCreate(originalUrl, UriKind.RelativeOrAbsolute, out var uri)) { - context.Request.Headers.Authorization = JwtBearerDefaults.AuthenticationScheme + ' ' + token; + if (QueryHelpers.ParseQuery(uri.Query).TryGetValue(QueryParameters.AccessToken, out var token) && + !context.Request.Headers.ContainsKey(HeaderNames.Authorization)) + { + context.Request.Headers.Authorization = JwtBearerDefaults.AuthenticationScheme + ' ' + token; + } } } } @@ -261,7 +263,7 @@ public static void Main(string[] args) (httpContext.Request.Query.TryGetValue(QueryParameters.SkipAuth, out var skipEquals) | httpContext.Request.Query.TryGetValue(QueryParameters.SkipAuthNe, out var skipNotEquals))) { - var originalUrl = GetOriginalUrl(httpContext.Request.Headers); + var originalUrl = GetOriginalUrl(httpContext); var originalMethod = GetOriginalMethod(httpContext.Request.Headers); if (skipEquals.Count > 0) @@ -458,17 +460,16 @@ public static void Main(string[] args) app.Run(); } - private static string? GetOriginalUrl(IHeaderDictionary headers) + private static string? GetOriginalUrl(HttpContext httpContext) { - if (headers.TryGetValue(CustomHeaderNames.XOriginalUrl, out var xOriginalUrl)) + if (httpContext.Request.Headers.TryGetValue(CustomHeaderNames.XOriginalUrl, out var xOriginalUrl)) { return xOriginalUrl!; } - else if (headers.TryGetValue(CustomHeaderNames.XForwardedProto, out var xForwardedProto) && - headers.TryGetValue(CustomHeaderNames.XForwardedHost, out var xForwardedHost) && - headers.TryGetValue(CustomHeaderNames.XForwardedUri, out var xForwardedUri)) + else if (httpContext.Request.Headers.TryGetValue(HeaderNames.Host, out var host) && + httpContext.Request.Headers.TryGetValue(CustomHeaderNames.XForwardedUri, out var xForwardedUri)) { - return $"{xForwardedProto}://{xForwardedHost}{xForwardedUri}"; + return $"{httpContext.Request.Scheme}://{host}{xForwardedUri}"; } return null; diff --git a/tests/oidc-guard-tests/AuthTests.cs b/tests/oidc-guard-tests/AuthTests.cs index 5838ec3..572e02c 100644 --- a/tests/oidc-guard-tests/AuthTests.cs +++ b/tests/oidc-guard-tests/AuthTests.cs @@ -458,6 +458,19 @@ public static IEnumerable GetTokenAsQueryParameterTests() {CustomHeaderNames.XOriginalUrl, $"https://www.example.com?{QueryParameters.AccessToken}={FakeJwtIssuer.GenerateJwtToken(new List{new Claim("tid", "22222222-2222-2222-2222-222222222222")})}" } }, }, + + new object[] // Token Only in Query String + { + "", + new List(), + HttpStatusCode.OK, + new Dictionary() + { + { CustomHeaderNames.XForwardedProto, "https" }, + { CustomHeaderNames.XForwardedHost, "www.example.com" }, + { CustomHeaderNames.XForwardedUri, $"?{QueryParameters.AccessToken}={FakeJwtIssuer.GenerateJwtToken(Enumerable.Empty())}" } + } + }, }; } @@ -547,6 +560,13 @@ public async Task UserInfoMulti() [InlineData("?skip-auth-ne=test", "https://bob.com", "GET", HttpStatusCode.OK)] [InlineData("?skip-auth-ne=test", "https://test.com", "GET", HttpStatusCode.Unauthorized)] public async Task SkipAuth(string query, string Url, string httpMethod, HttpStatusCode status) + { + await SkipAuthNginx(query, Url, httpMethod, status); + + await SkipAuthTraefik(query, Url, httpMethod, status); + } + + private async Task SkipAuthNginx(string query, string Url, string httpMethod, HttpStatusCode status) { var _client = AuthTestsHelpers.GetClient(); @@ -557,6 +577,19 @@ public async Task SkipAuth(string query, string Url, string httpMethod, HttpStat response.StatusCode.Should().Be(status); } + private async Task SkipAuthTraefik(string query, string Url, string httpMethod, HttpStatusCode status) + { + var _client = AuthTestsHelpers.GetClient(); + + _client.DefaultRequestHeaders.TryAddWithoutValidation(CustomHeaderNames.XForwardedHost, new Uri(Url).Host); + _client.DefaultRequestHeaders.TryAddWithoutValidation(CustomHeaderNames.XForwardedMethod, httpMethod); + _client.DefaultRequestHeaders.TryAddWithoutValidation(CustomHeaderNames.XForwardedProto, new Uri(Url).Scheme); + _client.DefaultRequestHeaders.TryAddWithoutValidation(CustomHeaderNames.XForwardedUri, "/"); + + var response = await _client.GetAsync($"/auth{query}"); + response.StatusCode.Should().Be(status); + } + [Fact] public async Task SetHost() { @@ -578,7 +611,7 @@ public async Task JWKSAuth() { x.Cookie.Enable = false; x.JWT.JWKSUrl = "https://inmemory.microsoft.com/common/discovery/keys"; - x.JWT.ValidIssuers = new string[] { FakeJwtIssuer.Issuer }; + x.JWT.ValidIssuers = [FakeJwtIssuer.Issuer]; }); _client.DefaultRequestHeaders.TryAddWithoutValidation(HeaderNames.Authorization, FakeJwtIssuer.GenerateBearerJwtToken(new List())); diff --git a/tests/oidc-guard-tests/EndToEnd/oidc-guard-values.yaml b/tests/oidc-guard-tests/EndToEnd/oidc-guard-values.yaml index edfc8df..ada1e52 100644 --- a/tests/oidc-guard-tests/EndToEnd/oidc-guard-values.yaml +++ b/tests/oidc-guard-tests/EndToEnd/oidc-guard-values.yaml @@ -88,7 +88,7 @@ settings: # Accept token from the access_token Query Parameter # https://datatracker.ietf.org/doc/html/rfc6750#section-2.3 - enableAccessTokenInQueryParameter: false + enableAccessTokenInQueryParameter: true # Prepend "Bearer " to the Authorization header if its missing prependBearer: false