Skip to content

Commit 8c39137

Browse files
pranavkmmkArtakMSFT
authored andcommitted
Port Throw when UseAuthorization is incorrectly configured (#14893)
* Port Throw when UseAuthorization is incorrectly configured * fixup
1 parent c0a7f04 commit 8c39137

File tree

9 files changed

+341
-38
lines changed

9 files changed

+341
-38
lines changed

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ public void ConfigureServices(IServiceCollection services)
2424
services.AddMvc().AddNewtonsoftJson();
2525
services.AddCors(options =>
2626
{
27-
options.AddPolicy("AllowAll", _ => { /* Controlled below */ });
27+
// It's not enough just to return "Access-Control-Allow-Origin: *", because
28+
// browsers don't allow wildcards in conjunction with credentials. So we must
29+
// specify explicitly which origin we want to allow.
30+
options.AddPolicy("AllowAll", policy =>
31+
policy.SetIsOriginAllowed(host => host.StartsWith("http://localhost:") || host.StartsWith("http://127.0.0.1:"))
32+
.AllowAnyHeader()
33+
.WithExposedHeaders("MyCustomHeader")
34+
.AllowAnyMethod()
35+
.AllowCredentials());
2836
});
2937
services.AddServerSideBlazor()
3038
.AddCircuitOptions(o =>
@@ -49,18 +57,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
4957
app.UseDeveloperExceptionPage();
5058
}
5159

52-
// It's not enough just to return "Access-Control-Allow-Origin: *", because
53-
// browsers don't allow wildcards in conjunction with credentials. So we must
54-
// specify explicitly which origin we want to allow.
55-
app.UseCors(policy =>
56-
{
57-
policy.SetIsOriginAllowed(host => host.StartsWith("http://localhost:") || host.StartsWith("http://127.0.0.1:"))
58-
.AllowAnyHeader()
59-
.WithExposedHeaders("MyCustomHeader")
60-
.AllowAnyMethod()
61-
.AllowCredentials();
62-
});
63-
6460
app.UseAuthentication();
6561

6662
// Mount the server-side Blazor app on /subdir
@@ -119,6 +115,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
119115
});
120116

121117
app.UseRouting();
118+
119+
app.UseCors();
122120

123121
app.UseEndpoints(endpoints =>
124122
{

src/Http/HttpAbstractions.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
113113
EndProject
114114
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebUtilities.Performance", "WebUtilities\perf\Microsoft.AspNetCore.WebUtilities.Performance\Microsoft.AspNetCore.WebUtilities.Performance.csproj", "{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}"
115115
EndProject
116+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization.Policy", "..\Security\Authorization\Policy\src\Microsoft.AspNetCore.Authorization.Policy.csproj", "{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}"
117+
EndProject
118+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cors", "..\Middleware\CORS\src\Microsoft.AspNetCore.Cors.csproj", "{09168958-FD5B-4D25-8FBF-75E2C80D903B}"
119+
EndProject
116120
Global
117121
GlobalSection(SolutionConfigurationPlatforms) = preSolution
118122
Debug|Any CPU = Debug|Any CPU
@@ -603,6 +607,30 @@ Global
603607
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x64.Build.0 = Release|Any CPU
604608
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x86.ActiveCfg = Release|Any CPU
605609
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E}.Release|x86.Build.0 = Release|Any CPU
610+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
611+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
612+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x64.ActiveCfg = Debug|Any CPU
613+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x64.Build.0 = Debug|Any CPU
614+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x86.ActiveCfg = Debug|Any CPU
615+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Debug|x86.Build.0 = Debug|Any CPU
616+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
617+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|Any CPU.Build.0 = Release|Any CPU
618+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x64.ActiveCfg = Release|Any CPU
619+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x64.Build.0 = Release|Any CPU
620+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x86.ActiveCfg = Release|Any CPU
621+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B}.Release|x86.Build.0 = Release|Any CPU
622+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
623+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|Any CPU.Build.0 = Debug|Any CPU
624+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x64.ActiveCfg = Debug|Any CPU
625+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x64.Build.0 = Debug|Any CPU
626+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x86.ActiveCfg = Debug|Any CPU
627+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Debug|x86.Build.0 = Debug|Any CPU
628+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|Any CPU.ActiveCfg = Release|Any CPU
629+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|Any CPU.Build.0 = Release|Any CPU
630+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x64.ActiveCfg = Release|Any CPU
631+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x64.Build.0 = Release|Any CPU
632+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x86.ActiveCfg = Release|Any CPU
633+
{09168958-FD5B-4D25-8FBF-75E2C80D903B}.Release|x86.Build.0 = Release|Any CPU
606634
EndGlobalSection
607635
GlobalSection(SolutionProperties) = preSolution
608636
HideSolutionNode = FALSE
@@ -651,6 +679,8 @@ Global
651679
{611794D2-EF3A-422A-A077-23E61C7ADE49} = {793FFE24-138A-4C3D-81AB-18D625E36230}
652680
{1062FCDE-E145-40EC-B175-FDBCAA0C59A0} = {793FFE24-138A-4C3D-81AB-18D625E36230}
653681
{21AC56E7-4E77-4B0E-B63E-C8E836E4D14E} = {80A090C8-ED02-4DE3-875A-30DCCDBD84BA}
682+
{8BCAA9EC-0ACD-435C-BF8A-8C843499FF7B} = {793FFE24-138A-4C3D-81AB-18D625E36230}
683+
{09168958-FD5B-4D25-8FBF-75E2C80D903B} = {793FFE24-138A-4C3D-81AB-18D625E36230}
654684
EndGlobalSection
655685
GlobalSection(ExtensibilityGlobals) = postSolution
656686
SolutionGuid = {85B5E151-2E9D-419C-83DD-0DDCF446C83A}

src/Http/Routing/src/EndpointMiddleware.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@
66
using Microsoft.AspNetCore.Authorization;
77
using Microsoft.AspNetCore.Cors.Infrastructure;
88
using Microsoft.AspNetCore.Http;
9-
using Microsoft.AspNetCore.Http.Features;
109
using Microsoft.Extensions.Logging;
1110
using Microsoft.Extensions.Options;
1211

1312
namespace Microsoft.AspNetCore.Routing
1413
{
1514
internal sealed class EndpointMiddleware
1615
{
17-
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareInvoked";
18-
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareInvoked";
16+
internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareWithEndpointInvoked";
17+
internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareWithEndpointInvoked";
1918

2019
private readonly ILogger _logger;
2120
private readonly RequestDelegate _next;
@@ -91,15 +90,15 @@ private static void ThrowMissingAuthMiddlewareException(Endpoint endpoint)
9190
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains authorization metadata, " +
9291
"but a middleware was not found that supports authorization." +
9392
Environment.NewLine +
94-
"Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code.");
93+
"Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).");
9594
}
9695

9796
private static void ThrowMissingCorsMiddlewareException(Endpoint endpoint)
9897
{
9998
throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains CORS metadata, " +
10099
"but a middleware was not found that supports CORS." +
101100
Environment.NewLine +
102-
"Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code.");
101+
"Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).");
103102
}
104103

105104
private static class Log
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Net;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Authorization;
8+
using Microsoft.AspNetCore.Builder;
9+
using Microsoft.AspNetCore.Hosting;
10+
using Microsoft.AspNetCore.Http;
11+
using Microsoft.AspNetCore.TestHost;
12+
using Microsoft.Extensions.DependencyInjection;
13+
using Xunit;
14+
15+
namespace Microsoft.AspNetCore.Routing.FunctionalTests
16+
{
17+
public class EndpointRoutingIntegrationTest
18+
{
19+
private static readonly RequestDelegate TestDelegate = async context => await Task.Yield();
20+
private static readonly string AuthErrorMessage = "Endpoint / contains authorization metadata, but a middleware was not found that supports authorization." +
21+
Environment.NewLine +
22+
"Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. " +
23+
"The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).";
24+
25+
private static readonly string CORSErrorMessage = "Endpoint / contains CORS metadata, but a middleware was not found that supports CORS." +
26+
Environment.NewLine +
27+
"Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. " +
28+
"The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).";
29+
30+
[Fact]
31+
public async Task AuthorizationMiddleware_WhenNoAuthMetadataIsConfigured()
32+
{
33+
// Arrange
34+
var builder = new WebHostBuilder();
35+
builder.Configure(app =>
36+
{
37+
app.UseRouting();
38+
app.UseAuthorization();
39+
app.UseEndpoints(b => b.Map("/", TestDelegate));
40+
})
41+
.ConfigureServices(services =>
42+
{
43+
services.AddAuthorization();
44+
services.AddRouting();
45+
});
46+
47+
using var server = new TestServer(builder);
48+
49+
var response = await server.CreateRequest("/").SendAsync("GET");
50+
51+
response.EnsureSuccessStatusCode();
52+
}
53+
54+
[Fact]
55+
public async Task AuthorizationMiddleware_WhenEndpointIsNotFound()
56+
{
57+
// Arrange
58+
var builder = new WebHostBuilder();
59+
builder.Configure(app =>
60+
{
61+
app.UseRouting();
62+
app.UseAuthorization();
63+
app.UseEndpoints(b => b.Map("/", TestDelegate));
64+
})
65+
.ConfigureServices(services =>
66+
{
67+
services.AddAuthorization();
68+
services.AddRouting();
69+
});
70+
71+
using var server = new TestServer(builder);
72+
73+
var response = await server.CreateRequest("/not-found").SendAsync("GET");
74+
75+
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
76+
}
77+
78+
[Fact]
79+
public async Task AuthorizationMiddleware_WithAuthorizedEndpoint()
80+
{
81+
// Arrange
82+
var builder = new WebHostBuilder();
83+
builder.Configure(app =>
84+
{
85+
app.UseRouting();
86+
app.UseAuthorization();
87+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
88+
})
89+
.ConfigureServices(services =>
90+
{
91+
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
92+
services.AddRouting();
93+
});
94+
95+
using var server = new TestServer(builder);
96+
97+
var response = await server.CreateRequest("/").SendAsync("GET");
98+
99+
response.EnsureSuccessStatusCode();
100+
}
101+
102+
[Fact]
103+
public async Task AuthorizationMiddleware_NotConfigured_Throws()
104+
{
105+
// Arrange
106+
var builder = new WebHostBuilder();
107+
builder.Configure(app =>
108+
{
109+
app.UseRouting();
110+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
111+
112+
})
113+
.ConfigureServices(services =>
114+
{
115+
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
116+
services.AddRouting();
117+
});
118+
119+
using var server = new TestServer(builder);
120+
121+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
122+
Assert.Equal(AuthErrorMessage, ex.Message);
123+
}
124+
125+
[Fact]
126+
public async Task AuthorizationMiddleware_NotConfigured_WhenEndpointIsNotFound()
127+
{
128+
// Arrange
129+
var builder = new WebHostBuilder();
130+
builder.Configure(app =>
131+
{
132+
app.UseRouting();
133+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
134+
})
135+
.ConfigureServices(services =>
136+
{
137+
services.AddRouting();
138+
});
139+
140+
using var server = new TestServer(builder);
141+
142+
var response = await server.CreateRequest("/not-found").SendAsync("GET");
143+
144+
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
145+
}
146+
147+
[Fact]
148+
public async Task AuthorizationMiddleware_ConfiguredBeforeRouting_Throws()
149+
{
150+
// Arrange
151+
var builder = new WebHostBuilder();
152+
builder.Configure(app =>
153+
{
154+
app.UseAuthorization();
155+
app.UseRouting();
156+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
157+
})
158+
.ConfigureServices(services =>
159+
{
160+
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
161+
services.AddRouting();
162+
});
163+
164+
using var server = new TestServer(builder);
165+
166+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
167+
Assert.Equal(AuthErrorMessage, ex.Message);
168+
}
169+
170+
[Fact]
171+
public async Task AuthorizationMiddleware_ConfiguredAfterRouting_Throws()
172+
{
173+
// Arrange
174+
var builder = new WebHostBuilder();
175+
builder.Configure(app =>
176+
{
177+
app.UseRouting();
178+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireAuthorization());
179+
app.UseAuthorization();
180+
})
181+
.ConfigureServices(services =>
182+
{
183+
services.AddAuthorization(options => options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build());
184+
services.AddRouting();
185+
});
186+
187+
using var server = new TestServer(builder);
188+
189+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
190+
Assert.Equal(AuthErrorMessage, ex.Message);
191+
}
192+
193+
[Fact]
194+
public async Task CorsMiddleware_WithCorsEndpoint()
195+
{
196+
// Arrange
197+
var builder = new WebHostBuilder();
198+
builder.Configure(app =>
199+
{
200+
app.UseRouting();
201+
app.UseCors();
202+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireCors(policy => policy.AllowAnyOrigin()));
203+
})
204+
.ConfigureServices(services =>
205+
{
206+
services.AddCors();
207+
services.AddRouting();
208+
});
209+
210+
using var server = new TestServer(builder);
211+
212+
var response = await server.CreateRequest("/").SendAsync("PUT");
213+
214+
response.EnsureSuccessStatusCode();
215+
}
216+
217+
[Fact]
218+
public async Task CorsMiddleware_ConfiguredBeforeRouting_Throws()
219+
{
220+
// Arrange
221+
var builder = new WebHostBuilder();
222+
builder.Configure(app =>
223+
{
224+
app.UseCors();
225+
app.UseRouting();
226+
app.UseEndpoints(b => b.Map("/", TestDelegate).RequireCors(policy => policy.AllowAnyOrigin()));
227+
})
228+
.ConfigureServices(services =>
229+
{
230+
services.AddCors();
231+
services.AddRouting();
232+
});
233+
234+
using var server = new TestServer(builder);
235+
236+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => server.CreateRequest("/").SendAsync("GET"));
237+
Assert.Equal(CORSErrorMessage, ex.Message);
238+
}
239+
}
240+
}

src/Http/Routing/test/FunctionalTests/Microsoft.AspNetCore.Routing.FunctionalTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
</ItemGroup>
1111

1212
<ItemGroup>
13+
<Reference Include="Microsoft.AspNetCore.Authorization.Policy" />
14+
<Reference Include="Microsoft.AspNetCore.Cors" />
1315
<Reference Include="Microsoft.AspNetCore.Routing" />
1416
<Reference Include="Microsoft.AspNetCore.TestHost" />
1517
</ItemGroup>

0 commit comments

Comments
 (0)