Skip to content

Remove Authentication property from WebApplicationBuilder and fix up middleware auto-registration #42585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/DefaultBuilder/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#nullable enable
Microsoft.AspNetCore.Builder.WebApplication.Use(System.Func<Microsoft.AspNetCore.Http.RequestDelegate!, Microsoft.AspNetCore.Http.RequestDelegate!>! middleware) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
Microsoft.AspNetCore.Builder.WebApplicationBuilder.Authentication.get -> Microsoft.AspNetCore.Authentication.AuthenticationBuilder!
static Microsoft.Extensions.Hosting.GenericHostBuilderExtensions.ConfigureWebHostDefaults(this Microsoft.Extensions.Hosting.IHostBuilder! builder, System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configure, System.Action<Microsoft.Extensions.Hosting.WebHostBuilderOptions!>! configureOptions) -> Microsoft.Extensions.Hosting.IHostBuilder!
48 changes: 0 additions & 48 deletions src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs

This file was deleted.

23 changes: 14 additions & 9 deletions src/DefaultBuilder/src/WebApplicationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -18,10 +19,10 @@ public sealed class WebApplicationBuilder
{
private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder";
private const string AuthenticationMiddlewareSetKey = "__AuthenticationMiddlewareSet";
private const string AuthorizationMiddlewareSetKey = "__AuthorizationMiddlewareSet";

private readonly HostApplicationBuilder _hostApplicationBuilder;
private readonly ServiceDescriptor _genericWebHostServiceDescriptor;
private readonly WebApplicationAuthenticationBuilder _webAuthBuilder;

private WebApplication? _builtApplication;

Expand Down Expand Up @@ -82,7 +83,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde

Host = new ConfigureHostBuilder(bootstrapHostBuilder.Context, Configuration, Services);
WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
_webAuthBuilder = new WebApplicationAuthenticationBuilder(Services);
}

/// <summary>
Expand Down Expand Up @@ -117,11 +117,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilde
/// </summary>
public ConfigureHostBuilder Host { get; }

/// <summary>
/// An <see cref="AuthenticationBuilder"/> for configuring authentication-related properties.
/// </summary>
public AuthenticationBuilder Authentication => _webAuthBuilder;

/// <summary>
/// Builds the <see cref="WebApplication"/>.
/// </summary>
Expand Down Expand Up @@ -175,15 +170,25 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
}
}

if (_webAuthBuilder.IsAuthenticationConfigured)
// Process authorization and authentication middlewares independently to avoid
// registering middlewares for services that do not exist
if (_builtApplication.Services.GetService<IAuthenticationSchemeProvider>() is not null)
{
// Don't add more than one instance of the middleware
if (!_builtApplication.Properties.ContainsKey(AuthenticationMiddlewareSetKey))
{
// The Use invocations will set the property on the outer pipeline,
// but we want to set it on the inner pipeline as well
// but we want to set it on the inner pipeline as well.
_builtApplication.Properties[AuthenticationMiddlewareSetKey] = true;
app.UseAuthentication();
}
}

if (_builtApplication.Services.GetService<IAuthorizationHandlerProvider>() is not null)
{
if (!_builtApplication.Properties.ContainsKey(AuthorizationMiddlewareSetKey))
{
_builtApplication.Properties[AuthorizationMiddlewareSetKey] = true;
app.UseAuthorization();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1990,11 +1990,9 @@ public async Task RegisterAuthMiddlewaresCorrectly()
var username = "foobar";

var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthenticationCore(o =>
{
o.DefaultScheme = "testSchemeName";
});
builder.Authentication.AddScheme<AuthenticationSchemeOptions, UberHandler>("testSchemeName", "testDisplayName", _ => { });
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("testSchemeName")
.AddScheme<AuthenticationSchemeOptions, UberHandler>("testSchemeName", "testDisplayName", _ => { });
builder.WebHost.UseTestServer();
await using var app = builder.Build();

Expand Down Expand Up @@ -2027,6 +2025,43 @@ public async Task RegisterAuthMiddlewaresCorrectly()
Assert.True(customMiddlewareExecuted);
}

[Fact]
public async Task SupportsDisablingMiddlewareAutoRegistration()
{
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("testSchemeName")
.AddScheme<AuthenticationSchemeOptions, UberHandler>("testSchemeName", "testDisplayName", _ => { });
builder.WebHost.UseTestServer();
await using var app = builder.Build();

app.Use(next =>
{
return async context =>
{
// IAuthenticationFeature is added by the authentication middleware
// during invocation. This middleware should run after authentication
// and be able to access the feature.
var authFeature = context.Features.Get<IAuthenticationFeature>();
Assert.Null(authFeature);
Assert.Null(context.User.Identity.Name);
await next(context);
};
});

app.Properties["__AuthenticationMiddlewareSet"] = true;

app.MapGet("/hello", (ClaimsPrincipal user) => {}).AllowAnonymous();

Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet"));

await app.StartAsync();

Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet"));
}

private class UberHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public UberHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;

var builder = WebApplication.CreateBuilder(args);

builder.Authentication.AddJwtBearer();
builder.Authentication.AddJwtBearer("ClaimedDetails");
builder.Authentication.AddJwtBearer("InvalidScheme");
builder.Services.AddAuthentication()
.AddJwtBearer()
.AddJwtBearer("ClaimedDetails")
.AddJwtBearer("InvalidScheme");

builder.Services.AddAuthorization(options =>
options.AddPolicy("is_admin", policy =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,33 +154,49 @@ public async Task IAuthenticateResultFeature_SettingResultSetsUser()
}

[Fact]
public async Task WebApplicationBuilder_RegistersAuthenticationMiddlewares()
public async Task WebApplicationBuilder_RegistersAuthenticationAndAuthorizationMiddlewares()
{
var builder = WebApplication.CreateBuilder();
builder.Configuration.AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ClaimsIssuer", "SomeIssuer"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:Audiences:0", "https://localhost:5001")
});
builder.Authentication.AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication().AddJwtBearer();
await using var app = builder.Build();

var webAppAuthBuilder = Assert.IsType<WebApplicationAuthenticationBuilder>(builder.Authentication);
Assert.True(webAppAuthBuilder.IsAuthenticationConfigured);

// Authentication middleware isn't registered until application
// is built on startup
Assert.False(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet"));

await app.StartAsync();

Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet"));

var options = app.Services.GetService<IOptionsMonitor<JwtBearerOptions>>().Get(JwtBearerDefaults.AuthenticationScheme);
Assert.Equal(new[] { "SomeIssuer" }, options.TokenValidationParameters.ValidIssuers);
Assert.Equal(new[] { "https://localhost:5001" }, options.TokenValidationParameters.ValidAudiences);
}

[Fact]
public async Task WebApplicationBuilder_OnlyRegistersMiddlewareWithSupportedServices()
{
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
await using var app = builder.Build();

Assert.False(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet"));

await app.StartAsync();

Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet"));
}

private HttpContext GetHttpContext(
Action<IServiceCollection> registerServices = null,
IAuthenticationService authenticationService = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace Microsoft.AspNetCore.Builder;
/// </summary>
public static class AuthorizationAppBuilderExtensions
{
internal const string AuthorizationMiddlewareSetKey = "__AuthorizationMiddlewareSet";

/// <summary>
/// Adds the <see cref="AuthorizationMiddleware"/> to the specified <see cref="IApplicationBuilder"/>, which enables authorization capabilities.
/// <para>
Expand All @@ -30,6 +32,7 @@ public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app)

VerifyServicesRegistered(app);

app.Properties[AuthorizationMiddlewareSetKey] = true;
return app.UseMiddleware<AuthorizationMiddleware>();
}

Expand Down