diff --git a/src/Components/Authorization/src/AuthenticationStateData.cs b/src/Components/Authorization/src/AuthenticationStateData.cs
index da4b5da8085d..bc83beac830c 100644
--- a/src/Components/Authorization/src/AuthenticationStateData.cs
+++ b/src/Components/Authorization/src/AuthenticationStateData.cs
@@ -13,7 +13,7 @@ public class AuthenticationStateData
///
/// The client-readable claims that describe the .
///
- public IList> Claims { get; set; } = [];
+ public IList Claims { get; set; } = [];
///
/// Gets the value that identifies 'Name' claims. This is used when returning the property .
diff --git a/src/Components/Authorization/src/ClaimData.cs b/src/Components/Authorization/src/ClaimData.cs
new file mode 100644
index 000000000000..b0ae8c4e2638
--- /dev/null
+++ b/src/Components/Authorization/src/ClaimData.cs
@@ -0,0 +1,45 @@
+// 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.Json.Serialization;
+
+namespace Microsoft.AspNetCore.Components.Authorization;
+
+///
+/// This is a serializable representation of a object that only consists of the type and value.
+///
+public readonly struct ClaimData
+{
+ ///
+ /// Constructs a new instance of from a type and value.
+ ///
+ /// The claim type.
+ /// The claim value
+ [JsonConstructor]
+ public ClaimData(string type, string value)
+ {
+ Type = type;
+ Value = value;
+ }
+
+ ///
+ /// Constructs a new instance of from a copying only the
+ /// and into their corresponding properties.
+ ///
+ /// The to copy from.
+ public ClaimData(Claim claim)
+ : this(claim.Type, claim.Value)
+ {
+ }
+
+ ///
+ /// Gets the claim type of the claim. .
+ ///
+ public string Type { get; }
+
+ ///
+ /// Gets the value of the claim.
+ ///
+ public string Value { get; }
+}
diff --git a/src/Components/Authorization/src/PublicAPI.Unshipped.txt b/src/Components/Authorization/src/PublicAPI.Unshipped.txt
index 74b5773a99d5..e02769f17caf 100644
--- a/src/Components/Authorization/src/PublicAPI.Unshipped.txt
+++ b/src/Components/Authorization/src/PublicAPI.Unshipped.txt
@@ -1,9 +1,15 @@
#nullable enable
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.AuthenticationStateData() -> void
-Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.Claims.get -> System.Collections.Generic.IList>!
+Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.Claims.get -> System.Collections.Generic.IList!
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.Claims.set -> void
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.NameClaimType.get -> string!
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.NameClaimType.set -> void
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.RoleClaimType.get -> string!
Microsoft.AspNetCore.Components.Authorization.AuthenticationStateData.RoleClaimType.set -> void
+Microsoft.AspNetCore.Components.Authorization.ClaimData
+Microsoft.AspNetCore.Components.Authorization.ClaimData.ClaimData() -> void
+Microsoft.AspNetCore.Components.Authorization.ClaimData.ClaimData(string! type, string! value) -> void
+Microsoft.AspNetCore.Components.Authorization.ClaimData.ClaimData(System.Security.Claims.Claim! claim) -> void
+Microsoft.AspNetCore.Components.Authorization.ClaimData.Type.get -> string!
+Microsoft.AspNetCore.Components.Authorization.ClaimData.Value.get -> string!
diff --git a/src/Components/WebAssembly/Server/src/AuthenticationStateSerializationOptions.cs b/src/Components/WebAssembly/Server/src/AuthenticationStateSerializationOptions.cs
index d7c3dc752a47..077bf134822a 100644
--- a/src/Components/WebAssembly/Server/src/AuthenticationStateSerializationOptions.cs
+++ b/src/Components/WebAssembly/Server/src/AuthenticationStateSerializationOptions.cs
@@ -51,19 +51,19 @@ public AuthenticationStateSerializationOptions()
{
foreach (var claim in authenticationState.User.Claims)
{
- data.Claims.Add(new(claim.Type, claim.Value));
+ data.Claims.Add(new(claim));
}
}
else
{
if (authenticationState.User.FindFirst(data.NameClaimType) is { } nameClaim)
{
- data.Claims.Add(new(nameClaim.Type, nameClaim.Value));
+ data.Claims.Add(new(nameClaim));
}
foreach (var roleClaim in authenticationState.User.FindAll(data.RoleClaimType))
{
- data.Claims.Add(new(roleClaim.Type, roleClaim.Value));
+ data.Claims.Add(new(roleClaim));
}
}
}
diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/AuthenticationStateDeserializationOptions.cs b/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/AuthenticationStateDeserializationOptions.cs
index 58bf7e903f50..d2e4207eee19 100644
--- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/AuthenticationStateDeserializationOptions.cs
+++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Options/AuthenticationStateDeserializationOptions.cs
@@ -31,7 +31,7 @@ private static Task DeserializeAuthenticationStateAsync(Aut
return Task.FromResult(
new AuthenticationState(new ClaimsPrincipal(
- new ClaimsIdentity(authenticationStateData.Claims.Select(c => new Claim(c.Key, c.Value)),
+ new ClaimsIdentity(authenticationStateData.Claims.Select(c => new Claim(c.Type, c.Value)),
authenticationType: nameof(DeserializedAuthenticationStateProvider),
nameType: authenticationStateData.NameClaimType,
roleType: authenticationStateData.RoleClaimType))));
diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs b/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs
index 065fffdb3951..bff25cd51fbe 100644
--- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs
+++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Services/DeserializedAuthenticationStateProvider.cs
@@ -5,6 +5,7 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Options;
+using static Microsoft.AspNetCore.Internal.LinkerFlags;
namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication;
@@ -21,7 +22,10 @@ internal sealed class DeserializedAuthenticationStateProvider : AuthenticationSt
[UnconditionalSuppressMessage(
"Trimming",
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
- Justification = $"{nameof(DeserializedAuthenticationStateProvider)} uses the {nameof(PersistentComponentState)} APIs to deserialize the token, which are already annotated.")]
+ Justification = $"{nameof(DeserializedAuthenticationStateProvider)} uses the {nameof(DynamicDependencyAttribute)} to preserve the necessary members.")]
+ [DynamicDependency(JsonSerialized, typeof(AuthenticationStateData))]
+ [DynamicDependency(JsonSerialized, typeof(IList))]
+ [DynamicDependency(JsonSerialized, typeof(ClaimData))]
public DeserializedAuthenticationStateProvider(PersistentComponentState state, IOptions options)
{
if (!state.TryTakeFromJson(PersistenceKey, out var authenticationStateData) || authenticationStateData is null)
diff --git a/src/Components/test/E2ETest/Infrastructure/ServerFixtures/BasicTestAppServerSiteFixture.cs b/src/Components/test/E2ETest/Infrastructure/ServerFixtures/BasicTestAppServerSiteFixture.cs
index e828bf69dfe6..20bb845423b8 100644
--- a/src/Components/test/E2ETest/Infrastructure/ServerFixtures/BasicTestAppServerSiteFixture.cs
+++ b/src/Components/test/E2ETest/Infrastructure/ServerFixtures/BasicTestAppServerSiteFixture.cs
@@ -7,6 +7,7 @@ public class BasicTestAppServerSiteFixture : AspNetSiteServerFixture w
{
public BasicTestAppServerSiteFixture()
{
+ ApplicationAssembly = typeof(TStartup).Assembly;
BuildWebHostMethod = TestServer.Program.BuildWebHost;
}
}
diff --git a/src/Components/test/E2ETest/Infrastructure/ServerFixtures/TrimmingServerFixture.cs b/src/Components/test/E2ETest/Infrastructure/ServerFixtures/TrimmingServerFixture.cs
new file mode 100644
index 000000000000..a0f0698cf450
--- /dev/null
+++ b/src/Components/test/E2ETest/Infrastructure/ServerFixtures/TrimmingServerFixture.cs
@@ -0,0 +1,55 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Testing;
+
+namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
+
+public class TrimmingServerFixture : BasicTestAppServerSiteFixture where TStartup : class
+{
+ public readonly bool TestTrimmedApps = typeof(ToggleExecutionModeServerFixture<>).Assembly
+ .GetCustomAttributes()
+ .First(m => m.Key == "Microsoft.AspNetCore.E2ETesting.TestTrimmedOrMultithreadingApps")
+ .Value == "true";
+
+ public TrimmingServerFixture()
+ {
+ if (TestTrimmedApps)
+ {
+ BuildWebHostMethod = BuildPublishedWebHost;
+ GetContentRootMethod = GetPublishedContentRoot;
+ }
+ }
+
+ private static IHost BuildPublishedWebHost(string[] args) =>
+ Extensions.Hosting.Host.CreateDefaultBuilder(args)
+ .ConfigureLogging((ctx, lb) =>
+ {
+ var sink = new TestSink();
+ lb.AddProvider(new TestLoggerProvider(sink));
+ lb.Services.AddSingleton(sink);
+ })
+ .ConfigureWebHostDefaults(webHostBuilder =>
+ {
+ webHostBuilder.UseStartup();
+ // Avoid UseStaticAssets or we won't use the trimmed published output.
+ })
+ .Build();
+
+ private static string GetPublishedContentRoot(Assembly assembly)
+ {
+ var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed-or-threading", assembly.GetName().Name);
+
+ if (!Directory.Exists(contentRoot))
+ {
+ throw new DirectoryNotFoundException($"Test is configured to use trimmed outputs, but trimmed outputs were not found in {contentRoot}.");
+ }
+
+ return contentRoot;
+ }
+}
diff --git a/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/DefaultAuthenticationStateSerializationOptionsTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/DefaultAuthenticationStateSerializationOptionsTest.cs
index 273f48f82b12..ebf67eb8ccf7 100644
--- a/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/DefaultAuthenticationStateSerializationOptionsTest.cs
+++ b/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/DefaultAuthenticationStateSerializationOptionsTest.cs
@@ -12,11 +12,11 @@
namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests.AuthTests;
public class DefaultAuthenticationStateSerializationOptionsTest
- : ServerTestBase>>
+ : ServerTestBase>>
{
public DefaultAuthenticationStateSerializationOptionsTest(
BrowserFixture browserFixture,
- BasicTestAppServerSiteFixture> serverFixture,
+ TrimmingServerFixture> serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
diff --git a/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/ServerRenderedAuthenticationStateTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/ServerRenderedAuthenticationStateTest.cs
index 931b2368c241..29fd3c4a9d2a 100644
--- a/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/ServerRenderedAuthenticationStateTest.cs
+++ b/src/Components/test/E2ETest/ServerRenderingTests/AuthTests/ServerRenderedAuthenticationStateTest.cs
@@ -12,11 +12,11 @@
namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests.AuthTests;
public class ServerRenderedAuthenticationStateTest
- : ServerTestBase>>
+ : ServerTestBase>>
{
public ServerRenderedAuthenticationStateTest(
BrowserFixture browserFixture,
- BasicTestAppServerSiteFixture> serverFixture,
+ TrimmingServerFixture> serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
diff --git a/src/Components/test/E2ETest/Tests/RemoteAuthenticationTest.cs b/src/Components/test/E2ETest/Tests/RemoteAuthenticationTest.cs
index 2ab1622379b8..95227c75759d 100644
--- a/src/Components/test/E2ETest/Tests/RemoteAuthenticationTest.cs
+++ b/src/Components/test/E2ETest/Tests/RemoteAuthenticationTest.cs
@@ -1,15 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Reflection;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Testing;
using OpenQA.Selenium;
using TestServer;
using Xunit.Abstractions;
@@ -17,26 +11,14 @@
namespace Microsoft.AspNetCore.Components.E2ETest.Tests;
public class RemoteAuthenticationTest :
- ServerTestBase>
+ ServerTestBase>
{
- public readonly bool TestTrimmedApps = typeof(ToggleExecutionModeServerFixture<>).Assembly
- .GetCustomAttributes()
- .First(m => m.Key == "Microsoft.AspNetCore.E2ETesting.TestTrimmedOrMultithreadingApps")
- .Value == "true";
-
public RemoteAuthenticationTest(
BrowserFixture browserFixture,
- BasicTestAppServerSiteFixture serverFixture,
+ TrimmingServerFixture serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
- serverFixture.ApplicationAssembly = typeof(RemoteAuthenticationStartup).Assembly;
-
- if (TestTrimmedApps)
- {
- serverFixture.BuildWebHostMethod = BuildPublishedWebHost;
- serverFixture.GetContentRootMethod = GetPublishedContentRoot;
- }
}
[Fact]
@@ -49,31 +31,4 @@ public void NavigateToLogin_PreservesExtraQueryParams()
var heading = Browser.Exists(By.TagName("h1"));
Browser.Equal("Hello, Jane Doe!", () => heading.Text);
}
-
- private static IHost BuildPublishedWebHost(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureLogging((ctx, lb) =>
- {
- var sink = new TestSink();
- lb.AddProvider(new TestLoggerProvider(sink));
- lb.Services.AddSingleton(sink);
- })
- .ConfigureWebHostDefaults(webHostBuilder =>
- {
- webHostBuilder.UseStartup();
- // Avoid UseStaticAssets or we won't use the trimmed published output.
- })
- .Build();
-
- private static string GetPublishedContentRoot(Assembly assembly)
- {
- var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed-or-threading", assembly.GetName().Name);
-
- if (!Directory.Exists(contentRoot))
- {
- throw new DirectoryNotFoundException($"Test is configured to use trimmed outputs, but trimmed outputs were not found in {contentRoot}.");
- }
-
- return contentRoot;
- }
}
diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs
index e0c0dd0c8a74..18869083cf0f 100644
--- a/src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs
+++ b/src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs
@@ -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.Reflection;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
@@ -10,26 +9,15 @@
namespace Microsoft.AspNetCore.Components.E2ETest.Tests;
-public class WebAssemblyPrerenderedTest : ServerTestBase
+public class WebAssemblyPrerenderedTest : ServerTestBase>
{
public WebAssemblyPrerenderedTest(
BrowserFixture browserFixture,
- AspNetSiteServerFixture serverFixture,
+ TrimmingServerFixture serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
- serverFixture.BuildWebHostMethod = Wasm.Prerendered.Server.Program.BuildWebHost;
serverFixture.Environment = AspNetEnvironment.Development;
-
- var testTrimmedApps = typeof(ToggleExecutionModeServerFixture<>).Assembly
- .GetCustomAttributes()
- .First(m => m.Key == "Microsoft.AspNetCore.E2ETesting.TestTrimmedOrMultithreadingApps")
- .Value == "true";
-
- if (testTrimmedApps)
- {
- serverFixture.GetContentRootMethod = GetPublishedContentRoot;
- }
}
[Fact]
@@ -53,16 +41,4 @@ private void WaitUntilLoaded()
var jsExecutor = (IJavaScriptExecutor)Browser;
Browser.True(() => jsExecutor.ExecuteScript("return window['__aspnetcore__testing__blazor_wasm__started__'];") is not null);
}
-
- private static string GetPublishedContentRoot(Assembly assembly)
- {
- var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed-or-threading", assembly.GetName().Name);
-
- if (!Directory.Exists(contentRoot))
- {
- throw new DirectoryNotFoundException($"Test is configured to use trimmed outputs, but trimmed outputs were not found in {contentRoot}.");
- }
-
- return contentRoot;
- }
}
diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs b/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs
index 7a9463136a36..943ee32d635c 100644
--- a/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs
+++ b/src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs
@@ -86,7 +86,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
_ = app.UseEndpoints(endpoints =>
{
- endpoints.MapStaticAssets();
+ var contentRootStaticAssetsPath = Path.Combine(env.ContentRootPath, "Components.TestServer.staticwebassets.endpoints.json");
+ if (File.Exists(contentRootStaticAssetsPath))
+ {
+ endpoints.MapStaticAssets(contentRootStaticAssetsPath);
+ }
+ else
+ {
+ endpoints.MapStaticAssets();
+ }
+
_ = endpoints.MapRazorComponents()
.AddAdditionalAssemblies(Assembly.Load("Components.WasmMinimal"))
.AddInteractiveServerRenderMode(options =>
diff --git a/src/Components/test/testassets/Components.TestServer/RemoteAuthenticationStartup.cs b/src/Components/test/testassets/Components.TestServer/RemoteAuthenticationStartup.cs
index 9c9641242521..e0ea4303ef8e 100644
--- a/src/Components/test/testassets/Components.TestServer/RemoteAuthenticationStartup.cs
+++ b/src/Components/test/testassets/Components.TestServer/RemoteAuthenticationStartup.cs
@@ -28,11 +28,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAntiforgery();
app.UseEndpoints(endpoints =>
{
-#if !DEBUG
- endpoints.MapStaticAssets(Path.Combine("trimmed-or-threading", "Components.TestServer", "Components.TestServer.staticwebassets.endpoints.json"));
-#else
- endpoints.MapStaticAssets("Components.TestServer.staticwebassets.endpoints.json");
-#endif
+ var contentRootStaticAssetsPath = Path.Combine(env.ContentRootPath, "Components.TestServer.staticwebassets.endpoints.json");
+ if (File.Exists(contentRootStaticAssetsPath))
+ {
+ endpoints.MapStaticAssets(contentRootStaticAssetsPath);
+ }
+ else
+ {
+ endpoints.MapStaticAssets();
+ }
+
endpoints.MapRazorComponents()
.AddAdditionalAssemblies(Assembly.Load("Components.WasmRemoteAuthentication"))
.AddInteractiveWebAssemblyRenderMode(options => options.PathPrefix = "/WasmRemoteAuthentication");