Skip to content

Commit 234ce65

Browse files
[release/8.2] Fix dashboard auth when unsecured (#5532)
* Fix dashboard auth when unsecured * Increase playwrite assert timeout and re-enable test * Comment --------- Co-authored-by: James Newton-King <[email protected]>
1 parent 75fdcff commit 234ce65

File tree

5 files changed

+48
-12
lines changed

5 files changed

+48
-12
lines changed

src/Aspire.Dashboard/Authentication/FrontendCompositeAuthenticationHandler.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Security.Claims;
54
using System.Text.Encodings.Web;
65
using Aspire.Dashboard.Authentication.Connection;
76
using Aspire.Dashboard.Configuration;
@@ -29,13 +28,6 @@ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
2928
parameters: new Dictionary<string, object?> { [AspirePolicyEvaluator.SuppressChallengeKey] = true }));
3029
}
3130

32-
var scheme = GetRelevantAuthenticationScheme();
33-
if (scheme == null)
34-
{
35-
var id = new ClaimsIdentity([new Claim(OtlpAuthorization.OtlpClaimName, bool.FalseString)]);
36-
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), Scheme.Name));
37-
}
38-
3931
result = await Context.AuthenticateAsync().ConfigureAwait(false);
4032
return result;
4133
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Security.Claims;
5+
using System.Text.Encodings.Web;
6+
using Microsoft.AspNetCore.Authentication;
7+
using Microsoft.Extensions.Options;
8+
9+
namespace Aspire.Dashboard.Authentication;
10+
11+
public class UnsecuredAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
12+
{
13+
public UnsecuredAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
14+
{
15+
}
16+
17+
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
18+
{
19+
var id = new ClaimsIdentity(
20+
[new Claim(ClaimTypes.NameIdentifier, "Local"), new Claim(FrontendAuthorizationDefaults.UnsecuredClaimName, bool.TrueString)],
21+
FrontendAuthenticationDefaults.AuthenticationSchemeUnsecured);
22+
23+
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), Scheme.Name)));
24+
}
25+
}

src/Aspire.Dashboard/DashboardWebApplication.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Aspire.Dashboard.Otlp.Http;
2222
using Aspire.Dashboard.Otlp.Storage;
2323
using Aspire.Hosting;
24+
using Microsoft.AspNetCore.Authentication;
2425
using Microsoft.AspNetCore.Authentication.Certificate;
2526
using Microsoft.AspNetCore.Authentication.Cookies;
2627
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
@@ -607,7 +608,7 @@ private static bool IsSameOrNull(Uri frontendUri, Uri? otlpUrl)
607608
private static void ConfigureAuthentication(WebApplicationBuilder builder, DashboardOptions dashboardOptions)
608609
{
609610
var authentication = builder.Services
610-
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
611+
.AddAuthentication(o => o.DefaultScheme = ConfigureDefaultAuthScheme(dashboardOptions))
611612
.AddScheme<FrontendCompositeAuthenticationHandlerOptions, FrontendCompositeAuthenticationHandler>(FrontendCompositeAuthenticationDefaults.AuthenticationScheme, o => { })
612613
.AddScheme<OtlpCompositeAuthenticationHandlerOptions, OtlpCompositeAuthenticationHandler>(OtlpCompositeAuthenticationDefaults.AuthenticationScheme, o => { })
613614
.AddScheme<OtlpApiKeyAuthenticationHandlerOptions, OtlpApiKeyAuthenticationHandler>(OtlpApiKeyAuthenticationDefaults.AuthenticationScheme, o => { })
@@ -728,6 +729,9 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder, Dashb
728729
options.Cookie.Name = DashboardAuthCookieName;
729730
});
730731
break;
732+
case FrontendAuthMode.Unsecured:
733+
authentication.AddScheme<AuthenticationSchemeOptions, UnsecuredAuthenticationHandler>(FrontendAuthenticationDefaults.AuthenticationSchemeUnsecured, o => { });
734+
break;
731735
}
732736

733737
builder.Services.AddAuthorization(options =>
@@ -758,13 +762,24 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder, Dashb
758762
options.AddPolicy(
759763
name: FrontendAuthorizationDefaults.PolicyName,
760764
policy: new AuthorizationPolicyBuilder(FrontendCompositeAuthenticationDefaults.AuthenticationScheme)
761-
.RequireClaim(OtlpAuthorization.OtlpClaimName, [bool.FalseString])
765+
.RequireClaim(FrontendAuthorizationDefaults.UnsecuredClaimName)
762766
.Build());
763767
break;
764768
default:
765769
throw new NotSupportedException($"Unexpected {nameof(FrontendAuthMode)} enum member: {dashboardOptions.Frontend.AuthMode}");
766770
}
767771
});
772+
773+
// ASP.NET Core authentication needs to have the correct default scheme for the configured frontend auth.
774+
// This is required for ASP.NET Core/SignalR/Blazor to flow the authenticated user from the request and into the dashboard app.
775+
static string ConfigureDefaultAuthScheme(DashboardOptions dashboardOptions)
776+
{
777+
return dashboardOptions.Frontend.AuthMode switch
778+
{
779+
FrontendAuthMode.Unsecured => FrontendAuthenticationDefaults.AuthenticationSchemeUnsecured,
780+
_ => CookieAuthenticationDefaults.AuthenticationScheme
781+
};
782+
}
768783
}
769784

770785
public int Run()
@@ -804,10 +819,12 @@ public static class FrontendAuthorizationDefaults
804819
{
805820
public const string PolicyName = "Frontend";
806821
public const string BrowserTokenClaimName = "BrowserTokenClaim";
822+
public const string UnsecuredClaimName = "UnsecuredTokenClaim";
807823
}
808824

809825
public static class FrontendAuthenticationDefaults
810826
{
811827
public const string AuthenticationSchemeOpenIdConnect = "FrontendOpenIdConnect";
812828
public const string AuthenticationSchemeBrowserToken = "FrontendBrowserToken";
829+
public const string AuthenticationSchemeUnsecured = "FrontendUnsecured";
813830
}

tests/Aspire.Dashboard.Tests/Integration/Playwright/AppBarTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ public AppBarTests(DashboardServerFixture dashboardServerFixture, PlaywrightFixt
1717
}
1818

1919
[Fact]
20-
[ActiveIssue("https://github.com/dotnet/aspire/issues/4851")]
2120
public async Task AppBar_Change_Theme()
2221
{
2322
// Arrange

tests/Aspire.Dashboard.Tests/Integration/Playwright/PlaywrightFixture.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Aspire.Workload.Tests;
@@ -13,6 +13,9 @@ public class PlaywrightFixture : IAsyncLifetime
1313

1414
public async Task InitializeAsync()
1515
{
16+
// Default timeout of 5000 ms could time out on slow CI servers.
17+
Assertions.SetDefaultExpectTimeout(15_000);
18+
1619
PlaywrightProvider.DetectAndSetInstalledPlaywrightDependenciesPath();
1720
Browser = await PlaywrightProvider.CreateBrowserAsync();
1821
}

0 commit comments

Comments
 (0)