Skip to content

Commit 2804cf7

Browse files
authored
Preserve RemoteAuthenticationContext during JS interop (#54225)
1 parent 38b8d1c commit 2804cf7

File tree

18 files changed

+355
-6
lines changed

18 files changed

+355
-6
lines changed

AspNetCore.sln

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Intern
17861786
EndProject
17871787
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NotReferencedInWasmCodePackage", "src\Components\test\testassets\NotReferencedInWasmCodePackage\NotReferencedInWasmCodePackage.csproj", "{433F91E4-E39D-4EB0-B798-2998B3969A2C}"
17881788
EndProject
1789+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Components.WasmRemoteAuthentication", "src\Components\test\testassets\Components.WasmRemoteAuthentication\Components.WasmRemoteAuthentication.csproj", "{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}"
1790+
EndProject
17891791
Global
17901792
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17911793
Debug|Any CPU = Debug|Any CPU
@@ -10771,6 +10773,22 @@ Global
1077110773
{433F91E4-E39D-4EB0-B798-2998B3969A2C}.Release|x64.Build.0 = Release|Any CPU
1077210774
{433F91E4-E39D-4EB0-B798-2998B3969A2C}.Release|x86.ActiveCfg = Release|Any CPU
1077310775
{433F91E4-E39D-4EB0-B798-2998B3969A2C}.Release|x86.Build.0 = Release|Any CPU
10776+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
10777+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|Any CPU.Build.0 = Debug|Any CPU
10778+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|arm64.ActiveCfg = Debug|Any CPU
10779+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|arm64.Build.0 = Debug|Any CPU
10780+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|x64.ActiveCfg = Debug|Any CPU
10781+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|x64.Build.0 = Debug|Any CPU
10782+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|x86.ActiveCfg = Debug|Any CPU
10783+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Debug|x86.Build.0 = Debug|Any CPU
10784+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|Any CPU.ActiveCfg = Release|Any CPU
10785+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|Any CPU.Build.0 = Release|Any CPU
10786+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|arm64.ActiveCfg = Release|Any CPU
10787+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|arm64.Build.0 = Release|Any CPU
10788+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x64.ActiveCfg = Release|Any CPU
10789+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x64.Build.0 = Release|Any CPU
10790+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x86.ActiveCfg = Release|Any CPU
10791+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13}.Release|x86.Build.0 = Release|Any CPU
1077410792
EndGlobalSection
1077510793
GlobalSection(SolutionProperties) = preSolution
1077610794
HideSolutionNode = FALSE
@@ -11653,6 +11671,7 @@ Global
1165311671
{B0A8E5D4-BC5A-448E-B222-431B6B2EB58E} = {05A169C7-4F20-4516-B10A-B13C5649D346}
1165411672
{15D08EA7-8C63-45FB-8B4D-C5F8E43B433E} = {05A169C7-4F20-4516-B10A-B13C5649D346}
1165511673
{433F91E4-E39D-4EB0-B798-2998B3969A2C} = {6126DCE4-9692-4EE2-B240-C65743572995}
11674+
{8A021D6D-7935-4AB3-BB47-38D4FF9B0D13} = {6126DCE4-9692-4EE2-B240-C65743572995}
1165611675
EndGlobalSection
1165711676
GlobalSection(ExtensibilityGlobals) = postSolution
1165811677
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

src/Components/Components.slnf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
"src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj",
3636
"src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj",
3737
"src\\Components\\WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj",
38-
"src\\Components\\WebAssembly\\testassets\\ThreadingApp\\ThreadingApp.csproj",
3938
"src\\Components\\WebAssembly\\testassets\\ThreadingApp.Server\\ThreadingApp.Server.csproj",
39+
"src\\Components\\WebAssembly\\testassets\\ThreadingApp\\ThreadingApp.csproj",
4040
"src\\Components\\WebAssembly\\testassets\\Wasm.Prerendered.Client\\Wasm.Prerendered.Client.csproj",
4141
"src\\Components\\WebAssembly\\testassets\\Wasm.Prerendered.Server\\Wasm.Prerendered.Server.csproj",
4242
"src\\Components\\WebAssembly\\testassets\\WasmLinkerTest\\WasmLinkerTest.csproj",
@@ -54,6 +54,7 @@
5454
"src\\Components\\test\\testassets\\BasicTestApp\\BasicTestApp.csproj",
5555
"src\\Components\\test\\testassets\\Components.TestServer\\Components.TestServer.csproj",
5656
"src\\Components\\test\\testassets\\Components.WasmMinimal\\Components.WasmMinimal.csproj",
57+
"src\\Components\\test\\testassets\\Components.WasmRemoteAuthentication\\Components.WasmRemoteAuthentication.csproj",
5758
"src\\Components\\test\\testassets\\ComponentsApp.App\\ComponentsApp.App.csproj",
5859
"src\\Components\\test\\testassets\\ComponentsApp.Server\\ComponentsApp.Server.csproj",
5960
"src\\Components\\test\\testassets\\GlobalizationWasmApp\\GlobalizationWasmApp.csproj",

src/Components/WebAssembly/WebAssembly.Authentication/src/Services/RemoteAuthenticationService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public virtual async Task<RemoteAuthenticationResult<TRemoteAuthenticationState>
108108
RemoteAuthenticationContext<TRemoteAuthenticationState> context)
109109
{
110110
await EnsureAuthService();
111-
var result = await JsRuntime.InvokeAsync<RemoteAuthenticationResult<TRemoteAuthenticationState>>("AuthenticationService.signIn", context);
111+
var result = await JSInvokeWithContextAsync<RemoteAuthenticationContext<TRemoteAuthenticationState>, RemoteAuthenticationResult<TRemoteAuthenticationState>>("AuthenticationService.signIn", context);
112112
await UpdateUserOnSuccess(result);
113113

114114
return result;
@@ -130,7 +130,7 @@ public virtual async Task<RemoteAuthenticationResult<TRemoteAuthenticationState>
130130
RemoteAuthenticationContext<TRemoteAuthenticationState> context)
131131
{
132132
await EnsureAuthService();
133-
var result = await JsRuntime.InvokeAsync<RemoteAuthenticationResult<TRemoteAuthenticationState>>("AuthenticationService.signOut", context);
133+
var result = await JSInvokeWithContextAsync<RemoteAuthenticationContext<TRemoteAuthenticationState>, RemoteAuthenticationResult<TRemoteAuthenticationState>>("AuthenticationService.signOut", context);
134134
await UpdateUserOnSuccess(result);
135135

136136
return result;
@@ -187,6 +187,11 @@ public virtual async ValueTask<AccessTokenResult> RequestAccessToken(AccessToken
187187
} : null);
188188
}
189189

190+
// JSRuntime.InvokeAsync does not properly annotate all arguments with DynamicallyAccessedMembersAttribute. https://github.com/dotnet/aspnetcore/issues/39839
191+
// Calling JsRuntime.InvokeAsync directly results allows the RemoteAuthenticationContext.State getter to be trimmed. https://github.com/dotnet/aspnetcore/issues/49956
192+
private ValueTask<TResult> JSInvokeWithContextAsync<[DynamicallyAccessedMembers(JsonSerialized)] TContext, [DynamicallyAccessedMembers(JsonSerialized)] TResult>(
193+
string identifier, TContext context) => JsRuntime.InvokeAsync<TResult>(identifier, context);
194+
190195
private string GetReturnUrl(string? customReturnUrl) =>
191196
customReturnUrl != null ? Navigation.ToAbsoluteUri(customReturnUrl).AbsoluteUri : Navigation.Uri;
192197

src/Components/test/E2ETest/Infrastructure/ServerFixtures/AspNetSiteServerFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ protected override IHost CreateWebHost()
3535
}
3636

3737
var assembly = ApplicationAssembly ?? BuildWebHostMethod.Method.DeclaringType.Assembly;
38-
var sampleSitePath = DefaultGetContentRoot(assembly);
38+
var sampleSitePath = GetContentRootMethod(assembly);
3939

4040
var host = "127.0.0.1";
4141
if (E2ETestOptions.Instance.SauceTest)

src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<!--
44
Skip building and running the Components E2E tests in CI unless explicitly configured otherwise via
@@ -92,6 +92,11 @@
9292
Include="..\..\WebAssembly\testassets\ThreadingApp\ThreadingApp.csproj"
9393
Targets="Build;Publish"
9494
Properties="BuildProjectReferences=false;TestTrimmedOrMultithreadingApps=false;PublishDir=$(MSBuildThisFileDirectory)$(OutputPath)trimmed-or-threading\ThreadingApp\;" />
95+
96+
<ProjectReference
97+
Include="..\testassets\Components.TestServer\Components.TestServer.csproj"
98+
Targets="Build;Publish"
99+
Properties="BuildProjectReferences=false;TestTrimmedOrMultithreadingApps=true;PublishDir=$(MSBuildThisFileDirectory)$(OutputPath)trimmed-or-threading\Components.TestServer\;" />
95100
</ItemGroup>
96101

97102
<!-- Shared testing infrastructure for running E2E tests using selenium -->
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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.Reflection;
5+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
6+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
7+
using Microsoft.AspNetCore.E2ETesting;
8+
using Microsoft.AspNetCore.Hosting;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Hosting;
11+
using Microsoft.Extensions.Logging;
12+
using Microsoft.Extensions.Logging.Testing;
13+
using OpenQA.Selenium;
14+
using TestServer;
15+
using Xunit.Abstractions;
16+
17+
namespace Microsoft.AspNetCore.Components.E2ETest.Tests;
18+
19+
public class RemoteAuthenticationTest :
20+
ServerTestBase<BasicTestAppServerSiteFixture<RemoteAuthenticationStartup>>
21+
{
22+
public readonly bool TestTrimmedApps = typeof(ToggleExecutionModeServerFixture<>).Assembly
23+
.GetCustomAttributes<AssemblyMetadataAttribute>()
24+
.First(m => m.Key == "Microsoft.AspNetCore.E2ETesting.TestTrimmedOrMultithreadingApps")
25+
.Value == "true";
26+
27+
public RemoteAuthenticationTest(
28+
BrowserFixture browserFixture,
29+
BasicTestAppServerSiteFixture<RemoteAuthenticationStartup> serverFixture,
30+
ITestOutputHelper output)
31+
: base(browserFixture, serverFixture, output)
32+
{
33+
serverFixture.ApplicationAssembly = typeof(RemoteAuthenticationStartup).Assembly;
34+
35+
if (TestTrimmedApps)
36+
{
37+
serverFixture.BuildWebHostMethod = BuildPublishedWebHost;
38+
serverFixture.GetContentRootMethod = GetPublishedContentRoot;
39+
}
40+
}
41+
42+
[Fact]
43+
public void NavigateToLogin_PreservesExtraQueryParams()
44+
{
45+
// If the preservedExtraQueryParams passed to NavigateToLogin by RedirectToLogin gets trimmed,
46+
// the OIDC endpoints will fail to authenticate the user.
47+
Navigate("/subdir/test-remote-authentication");
48+
49+
var heading = Browser.Exists(By.TagName("h1"));
50+
Browser.Equal("Hello, Jane Doe!", () => heading.Text);
51+
}
52+
53+
private static IHost BuildPublishedWebHost(string[] args) =>
54+
Host.CreateDefaultBuilder(args)
55+
.ConfigureLogging((ctx, lb) =>
56+
{
57+
TestSink sink = new TestSink();
58+
lb.AddProvider(new TestLoggerProvider(sink));
59+
lb.Services.AddSingleton(sink);
60+
})
61+
.ConfigureWebHostDefaults(webHostBuilder =>
62+
{
63+
webHostBuilder.UseStartup<RemoteAuthenticationStartup>();
64+
// Avoid UseStaticAssets or we won't use the trimmed published output.
65+
})
66+
.Build();
67+
68+
private static string GetPublishedContentRoot(Assembly assembly)
69+
{
70+
var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed-or-threading", assembly.GetName().Name);
71+
72+
if (!Directory.Exists(contentRoot))
73+
{
74+
throw new DirectoryNotFoundException($"Test is configured to use trimmed outputs, but trimmed outputs were not found in {contentRoot}.");
75+
}
76+
77+
return contentRoot;
78+
}
79+
}

src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private void WaitUntilLoaded()
5656

5757
private static string GetPublishedContentRoot(Assembly assembly)
5858
{
59-
var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed", assembly.GetName().Name);
59+
var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed-or-threading", assembly.GetName().Name);
6060

6161
if (!Directory.Exists(contentRoot))
6262
{

src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
<Reference Include="Microsoft.AspNetCore.SignalR" />
2929
<Reference Include="Microsoft.AspNetCore.InternalTesting" />
3030
<Reference Include="Microsoft.Extensions.Hosting" />
31+
<Reference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" />
3132
</ItemGroup>
3233

3334
<ItemGroup>
3435
<ProjectReference Include="..\BasicTestApp\BasicTestApp.csproj" />
3536
<ProjectReference Include="..\Components.WasmMinimal\Components.WasmMinimal.csproj" />
37+
<ProjectReference Include="..\Components.WasmRemoteAuthentication\Components.WasmRemoteAuthentication.csproj" />
3638
</ItemGroup>
3739

3840
<ItemGroup>

src/Components/test/testassets/Components.TestServer/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static async Task Main(string[] args)
1919
var createIndividualHosts = new Dictionary<string, (IHost host, string basePath)>
2020
{
2121
["Client authentication"] = (BuildWebHost<AuthenticationStartup>(CreateAdditionalArgs(args)), "/subdir"),
22+
["Remote client authentication"] = (BuildWebHost<RemoteAuthenticationStartup>(CreateAdditionalArgs(args)), "/subdir"),
2223
["Server authentication"] = (BuildWebHost<ServerAuthenticationStartup>(CreateAdditionalArgs(args)), "/subdir"),
2324
["CORS (WASM)"] = (BuildWebHost<CorsStartup>(CreateAdditionalArgs(args)), "/subdir"),
2425
["Prerendering (Server-side)"] = (BuildWebHost<PrerenderedStartup>(CreateAdditionalArgs(args)), "/prerendered"),
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<base href="/subdir/" />
7+
8+
<HeadOutlet @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
9+
</head>
10+
11+
<body>
12+
<Components.WasmRemoteAuthentication.Routes @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
13+
<script src="_framework/blazor.web.js" autostart="false"></script>
14+
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
15+
<script>
16+
Blazor.start({
17+
webAssembly: {
18+
loadBootResource: (type, name, defaultUri, integrity) => `WasmRemoteAuthentication/_framework/${name}`
19+
}
20+
});
21+
</script>
22+
</body>
23+
24+
</html>

0 commit comments

Comments
 (0)