diff --git a/Directory.Packages.props b/Directory.Packages.props
index a553461eb27..c80124920f5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -57,6 +57,7 @@
+
diff --git a/eng/Versions.props b/eng/Versions.props
index c9269f86ce7..ced6d07c914 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -49,6 +49,7 @@
8.0.7
8.0.7
8.0.7
+ 8.0.7
8.0.7
8.0.7
8.0.7
diff --git a/src/Aspire.Dashboard/Model/ValidateTokenMiddleware.cs b/src/Aspire.Dashboard/Model/ValidateTokenMiddleware.cs
index 70a49ab972a..e741099f7b1 100644
--- a/src/Aspire.Dashboard/Model/ValidateTokenMiddleware.cs
+++ b/src/Aspire.Dashboard/Model/ValidateTokenMiddleware.cs
@@ -26,22 +26,21 @@ public ValidateTokenMiddleware(RequestDelegate next, IOptionsMonitor>();
if (await TryAuthenticateAsync(value.ToString(), context, dashboardOptions).ConfigureAwait(false))
{
// Success. Redirect to the app.
- if (context.Request.Query.TryGetValue("returnUrl", out var returnUrl))
- {
- context.Response.Redirect(returnUrl.ToString());
- }
- else
- {
- context.Response.Redirect(DashboardUrls.ResourcesUrl());
- }
+ RedirectAfterValidation(context);
}
else
{
@@ -62,15 +61,23 @@ public async Task InvokeAsync(HttpContext context)
return;
}
- else
- {
- _logger.LogDebug($"Request to validate token URL but auth mode isn't set to {FrontendAuthMode.BrowserToken}.");
- }
}
await _next(context).ConfigureAwait(false);
}
+ private static void RedirectAfterValidation(HttpContext context)
+ {
+ if (context.Request.Query.TryGetValue("returnUrl", out var returnUrl))
+ {
+ context.Response.Redirect(returnUrl.ToString());
+ }
+ else
+ {
+ context.Response.Redirect(DashboardUrls.ResourcesUrl());
+ }
+ }
+
public static async Task TryAuthenticateAsync(string incomingBrowserToken, HttpContext httpContext, IOptionsMonitor dashboardOptions)
{
if (string.IsNullOrEmpty(incomingBrowserToken) || dashboardOptions.CurrentValue.Frontend.GetBrowserTokenBytes() is not { } expectedBrowserTokenBytes)
diff --git a/tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj b/tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj
index 001879a9549..9cbf298ca5f 100644
--- a/tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj
+++ b/tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj
@@ -15,6 +15,7 @@
+
diff --git a/tests/Aspire.Dashboard.Tests/Middleware/ValidateTokenMiddlewareTests.cs b/tests/Aspire.Dashboard.Tests/Middleware/ValidateTokenMiddlewareTests.cs
new file mode 100644
index 00000000000..77add73762e
--- /dev/null
+++ b/tests/Aspire.Dashboard.Tests/Middleware/ValidateTokenMiddlewareTests.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Aspire.Dashboard.Configuration;
+using Aspire.Dashboard.Model;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Xunit;
+
+namespace Aspire.Dashboard.Tests.Middleware;
+
+public class ValidateTokenMiddlewareTests
+{
+ [Fact]
+ public async Task ValidateToken_NotBrowserTokenAuth_RedirectedToHomepage()
+ {
+ using var host = await SetUpHostAsync(FrontendAuthMode.Unsecured, string.Empty);
+ var response = await host.GetTestClient().GetAsync("/login?t=test");
+ Assert.Equal("/", response.Headers.Location?.OriginalString);
+ }
+
+ [Fact]
+ public async Task ValidateToken_NotBrowserTokenAuth_RedirectedToReturnUrl()
+ {
+ using var host = await SetUpHostAsync(FrontendAuthMode.Unsecured, string.Empty);
+ var response = await host.GetTestClient().GetAsync("/login?t=test&returnUrl=/test");
+ Assert.Equal("/test", response.Headers.Location?.OriginalString);
+ }
+
+ [Fact]
+ public async Task ValidateToken_BrowserTokenAuth_WrongToken_RedirectsToLogin()
+ {
+ using var host = await SetUpHostAsync(FrontendAuthMode.BrowserToken, "token");
+ var response = await host.GetTestClient().GetAsync("/login?t=wrong");
+ Assert.Equal("/login", response.Headers.Location?.OriginalString);
+ }
+
+ [Fact]
+ public async Task ValidateToken_BrowserTokenAuth_WrongToken_RedirectsToLogin_WithReturnUrl()
+ {
+ using var host = await SetUpHostAsync(FrontendAuthMode.BrowserToken, "token");
+ var response = await host.GetTestClient().GetAsync("/login?t=wrong&returnUrl=/test");
+ Assert.Equal("/login?returnUrl=%2ftest", response.Headers.Location?.OriginalString);
+ }
+
+ [Fact]
+ public async Task ValidateToken_BrowserTokenAuth_RightToken_RedirectsToHome()
+ {
+ using var host = await SetUpHostAsync(FrontendAuthMode.BrowserToken, "token");
+ var response = await host.GetTestClient().GetAsync("/login?t=token");
+ Assert.Equal("/", response.Headers.Location?.OriginalString);
+ }
+
+ [Fact]
+ public async Task ValidateToken_BrowserTokenAuth_RightToken_RedirectsToReturnUrl()
+ {
+ using var host = await SetUpHostAsync(FrontendAuthMode.BrowserToken, "token");
+ var response = await host.GetTestClient().GetAsync("/login?t=token&returnUrl=/test");
+ Assert.Equal("/test", response.Headers.Location?.OriginalString);
+ }
+
+ private static async Task SetUpHostAsync(FrontendAuthMode authMode, string expectedToken)
+ {
+ return await new HostBuilder()
+ .ConfigureWebHost(webBuilder =>
+ {
+ webBuilder
+ .UseTestServer()
+ .ConfigureServices(services =>
+ {
+ services.AddRouting();
+ services.AddAuthentication().AddCookie();
+
+ services.Configure(o =>
+ {
+ o.Frontend = new FrontendOptions
+ {
+ AuthMode = authMode,
+ BrowserToken = expectedToken,
+ EndpointUrls = "http://localhost/" // required for TryParseOptions
+ };
+
+ Assert.True(o.Frontend.TryParseOptions(out _));
+ });
+ })
+ .Configure(app =>
+ {
+ app.UseMiddleware();
+ });
+ })
+ .StartAsync();
+ }
+}