Skip to content

Commit

Permalink
[release/8.2] Fix dashboard auth when unsecured (#5532)
Browse files Browse the repository at this point in the history
* Fix dashboard auth when unsecured

* Increase playwrite assert timeout and re-enable test

* Comment

---------

Co-authored-by: James Newton-King <james@newtonking.com>
  • Loading branch information
github-actions[bot] and JamesNK authored Sep 4, 2024
1 parent 75fdcff commit 234ce65
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

var scheme = GetRelevantAuthenticationScheme();
if (scheme == null)
{
var id = new ClaimsIdentity([new Claim(OtlpAuthorization.OtlpClaimName, bool.FalseString)]);
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), Scheme.Name));
}

result = await Context.AuthenticateAsync().ConfigureAwait(false);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

namespace Aspire.Dashboard.Authentication;

public class UnsecuredAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public UnsecuredAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
{
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var id = new ClaimsIdentity(
[new Claim(ClaimTypes.NameIdentifier, "Local"), new Claim(FrontendAuthorizationDefaults.UnsecuredClaimName, bool.TrueString)],
FrontendAuthenticationDefaults.AuthenticationSchemeUnsecured);

return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), Scheme.Name)));
}
}
21 changes: 19 additions & 2 deletions src/Aspire.Dashboard/DashboardWebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Aspire.Dashboard.Otlp.Http;
using Aspire.Dashboard.Otlp.Storage;
using Aspire.Hosting;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
Expand Down Expand Up @@ -607,7 +608,7 @@ private static bool IsSameOrNull(Uri frontendUri, Uri? otlpUrl)
private static void ConfigureAuthentication(WebApplicationBuilder builder, DashboardOptions dashboardOptions)
{
var authentication = builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddAuthentication(o => o.DefaultScheme = ConfigureDefaultAuthScheme(dashboardOptions))
.AddScheme<FrontendCompositeAuthenticationHandlerOptions, FrontendCompositeAuthenticationHandler>(FrontendCompositeAuthenticationDefaults.AuthenticationScheme, o => { })
.AddScheme<OtlpCompositeAuthenticationHandlerOptions, OtlpCompositeAuthenticationHandler>(OtlpCompositeAuthenticationDefaults.AuthenticationScheme, o => { })
.AddScheme<OtlpApiKeyAuthenticationHandlerOptions, OtlpApiKeyAuthenticationHandler>(OtlpApiKeyAuthenticationDefaults.AuthenticationScheme, o => { })
Expand Down Expand Up @@ -728,6 +729,9 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder, Dashb
options.Cookie.Name = DashboardAuthCookieName;
});
break;
case FrontendAuthMode.Unsecured:
authentication.AddScheme<AuthenticationSchemeOptions, UnsecuredAuthenticationHandler>(FrontendAuthenticationDefaults.AuthenticationSchemeUnsecured, o => { });
break;
}

builder.Services.AddAuthorization(options =>
Expand Down Expand Up @@ -758,13 +762,24 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder, Dashb
options.AddPolicy(
name: FrontendAuthorizationDefaults.PolicyName,
policy: new AuthorizationPolicyBuilder(FrontendCompositeAuthenticationDefaults.AuthenticationScheme)
.RequireClaim(OtlpAuthorization.OtlpClaimName, [bool.FalseString])
.RequireClaim(FrontendAuthorizationDefaults.UnsecuredClaimName)
.Build());
break;
default:
throw new NotSupportedException($"Unexpected {nameof(FrontendAuthMode)} enum member: {dashboardOptions.Frontend.AuthMode}");
}
});

// ASP.NET Core authentication needs to have the correct default scheme for the configured frontend auth.
// This is required for ASP.NET Core/SignalR/Blazor to flow the authenticated user from the request and into the dashboard app.
static string ConfigureDefaultAuthScheme(DashboardOptions dashboardOptions)
{
return dashboardOptions.Frontend.AuthMode switch
{
FrontendAuthMode.Unsecured => FrontendAuthenticationDefaults.AuthenticationSchemeUnsecured,
_ => CookieAuthenticationDefaults.AuthenticationScheme
};
}
}

public int Run()
Expand Down Expand Up @@ -804,10 +819,12 @@ public static class FrontendAuthorizationDefaults
{
public const string PolicyName = "Frontend";
public const string BrowserTokenClaimName = "BrowserTokenClaim";
public const string UnsecuredClaimName = "UnsecuredTokenClaim";
}

public static class FrontendAuthenticationDefaults
{
public const string AuthenticationSchemeOpenIdConnect = "FrontendOpenIdConnect";
public const string AuthenticationSchemeBrowserToken = "FrontendBrowserToken";
public const string AuthenticationSchemeUnsecured = "FrontendUnsecured";
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public AppBarTests(DashboardServerFixture dashboardServerFixture, PlaywrightFixt
}

[Fact]
[ActiveIssue("https://github.com/dotnet/aspire/issues/4851")]
public async Task AppBar_Change_Theme()
{
// Arrange
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

public async Task InitializeAsync()
{
// Default timeout of 5000 ms could time out on slow CI servers.
Assertions.SetDefaultExpectTimeout(15_000);

PlaywrightProvider.DetectAndSetInstalledPlaywrightDependenciesPath();
Browser = await PlaywrightProvider.CreateBrowserAsync();
}
Expand Down

0 comments on commit 234ce65

Please sign in to comment.