Skip to content

Commit

Permalink
Port fix for #53423 to main (#56702)
Browse files Browse the repository at this point in the history
  • Loading branch information
MackinnonBuck authored Jul 11, 2024
1 parent 6aab04c commit f3db10e
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ public void Configure(string? name, JwtBearerOptions options)
return;
}

var validateIssuer = StringHelpers.ParseValueOrDefault(configSection[nameof(TokenValidationParameters.ValidateIssuer)], bool.Parse, options.TokenValidationParameters.ValidateIssuer);
var issuer = configSection[nameof(TokenValidationParameters.ValidIssuer)];
var issuers = configSection.GetSection(nameof(TokenValidationParameters.ValidIssuers)).GetChildren().Select(iss => iss.Value).ToList();
var validateAudience = StringHelpers.ParseValueOrDefault(configSection[nameof(TokenValidationParameters.ValidateAudience)], bool.Parse, options.TokenValidationParameters.ValidateAudience);
var audience = configSection[nameof(TokenValidationParameters.ValidAudience)];
var audiences = configSection.GetSection(nameof(TokenValidationParameters.ValidAudiences)).GetChildren().Select(aud => aud.Value).ToList();

Expand All @@ -63,10 +65,10 @@ public void Configure(string? name, JwtBearerOptions options)
options.SaveToken = StringHelpers.ParseValueOrDefault(configSection[nameof(options.SaveToken)], bool.Parse, options.SaveToken);
options.TokenValidationParameters = new()
{
ValidateIssuer = issuers.Count > 0,
ValidateIssuer = validateIssuer,
ValidIssuers = issuers,
ValidIssuer = issuer,
ValidateAudience = audiences.Count > 0,
ValidateAudience = validateAudience,
ValidAudiences = audiences,
ValidAudience = audience,
ValidateIssuerSigningKey = true,
Expand Down
119 changes: 73 additions & 46 deletions src/Security/Authentication/test/JwtBearerTests_Handler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,9 @@ public class JwtBearerTests_Handler : SharedAuthenticationTests<JwtBearerOptions
protected override bool SupportsSignIn { get => false; }
protected override bool SupportsSignOut { get => false; }

protected override void RegisterAuth(AuthenticationBuilder services, Action<JwtBearerOptions> configure)
{
services.AddJwtBearer(o =>
{
ConfigureDefaults(o);
configure.Invoke(o);
});
}

private void ConfigureDefaults(JwtBearerOptions o)
protected override void RegisterAuth(AuthenticationBuilder services, Action<JwtBearerOptions> configure = null)
{
services.AddJwtBearer(configure);
}

[Fact]
Expand Down Expand Up @@ -964,26 +956,20 @@ public async Task ExpirationAndIssuedWhenMinOrMaxValue()
[Fact]
public void CanReadJwtBearerOptionsFromConfig()
{
var services = new ServiceCollection().AddLogging();
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ValidIssuer", "dotnet-user-jwts"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ValidIssuers:0", "dotnet-user-jwts-2"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ValidAudience", "http://localhost:5000"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ValidAudiences:0", "http://localhost:5001"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:BackchannelTimeout", "00:01:00"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:RequireHttpsMetadata", "false"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SaveToken", "True"),
}).Build();
var services = new ServiceCollection();
var config = new ConfigurationBuilder().AddInMemoryCollection([
new("Authentication:Schemes:Bearer:ValidIssuer", "dotnet-user-jwts"),
new("Authentication:Schemes:Bearer:ValidIssuers:0", "dotnet-user-jwts-2"),
new("Authentication:Schemes:Bearer:ValidAudience", "http://localhost:5000"),
new("Authentication:Schemes:Bearer:ValidAudiences:0", "http://localhost:5001"),
new("Authentication:Schemes:Bearer:BackchannelTimeout", "00:01:00"),
new("Authentication:Schemes:Bearer:RequireHttpsMetadata", "false"),
new("Authentication:Schemes:Bearer:SaveToken", "True"),
]).Build();
services.AddSingleton<IConfiguration>(config);

// Act
var builder = services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("Bearer", "Bearer");
});
builder.AddJwtBearer("Bearer");
RegisterAuth(builder, _ => { });
RegisterAuth(services.AddAuthentication());
var sp = services.BuildServiceProvider();

// Assert
Expand All @@ -995,7 +981,12 @@ public void CanReadJwtBearerOptionsFromConfig()
Assert.Equal(TimeSpan.FromSeconds(60), jwtBearerOptions.BackchannelTimeout);
Assert.False(jwtBearerOptions.RequireHttpsMetadata);
Assert.True(jwtBearerOptions.SaveToken);
Assert.True(jwtBearerOptions.MapInboundClaims); // Assert default values are respected
// ValidateIssuerSigningKey should always be set to its non-default value of true if options are read from config.
Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateIssuerSigningKey);
// Assert default values for other options are respected.
Assert.True(jwtBearerOptions.MapInboundClaims);
Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateIssuer);
Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateAudience);
}

[Fact]
Expand Down Expand Up @@ -1026,29 +1017,23 @@ public void CanReadMultipleAudiencesFromConfig()
[Fact]
public void CanReadMultipleIssuersFromConfig()
{
var services = new ServiceCollection().AddLogging();
var services = new ServiceCollection();
var firstKey = "qPG6tDtfxFYZifHW3sEueQ==";
var secondKey = "6JPzXj6aOPdojlZdeLshaA==";
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ValidIssuers:0", "dotnet-user-jwts"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ValidIssuers:1", "dotnet-user-jwts-2"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SigningKeys:0:Issuer", "dotnet-user-jwts"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SigningKeys:0:Value", firstKey),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SigningKeys:0:Length", "32"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SigningKeys:1:Issuer", "dotnet-user-jwts-2"),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SigningKeys:1:Value", secondKey),
new KeyValuePair<string, string>("Authentication:Schemes:Bearer:SigningKeys:1:Length", "32"),
}).Build();
var config = new ConfigurationBuilder().AddInMemoryCollection([
new("Authentication:Schemes:Bearer:ValidIssuers:0", "dotnet-user-jwts"),
new("Authentication:Schemes:Bearer:ValidIssuers:1", "dotnet-user-jwts-2"),
new("Authentication:Schemes:Bearer:SigningKeys:0:Issuer", "dotnet-user-jwts"),
new("Authentication:Schemes:Bearer:SigningKeys:0:Value", firstKey),
new("Authentication:Schemes:Bearer:SigningKeys:0:Length", "32"),
new("Authentication:Schemes:Bearer:SigningKeys:1:Issuer", "dotnet-user-jwts-2"),
new("Authentication:Schemes:Bearer:SigningKeys:1:Value", secondKey),
new("Authentication:Schemes:Bearer:SigningKeys:1:Length", "32"),
]).Build();
services.AddSingleton<IConfiguration>(config);

// Act
var builder = services.AddAuthentication(o =>
{
o.AddScheme<TestHandler>("Bearer", "Bearer");
});
builder.AddJwtBearer("Bearer");
RegisterAuth(builder, _ => { });
RegisterAuth(services.AddAuthentication());
var sp = services.BuildServiceProvider();

// Assert
Expand All @@ -1058,6 +1043,48 @@ public void CanReadMultipleIssuersFromConfig()
Assert.Equal(secondKey, Convert.ToBase64String(jwtBearerOptions.TokenValidationParameters.IssuerSigningKeys.OfType<SymmetricSecurityKey>().LastOrDefault()?.Key));
}

[Fact]
public void IssuerAndAudienceValidationEnabledByDefaultWhenOptionsAreReadFromConfig()
{
var services = new ServiceCollection();
var config = new ConfigurationBuilder().AddInMemoryCollection([
new("Authentication:Schemes:Bearer:Authority", "https://localhost:5001"),
]).Build();
services.AddSingleton<IConfiguration>(config);

// Act
RegisterAuth(services.AddAuthentication());
var sp = services.BuildServiceProvider();

// Assert
var jwtBearerOptions = sp.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>().Get(JwtBearerDefaults.AuthenticationScheme);
Assert.Equal("https://localhost:5001", jwtBearerOptions.Authority);
Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateIssuer);
Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateAudience);
}

[Fact]
public void IssuerAndAudienceValidationCanBeDisabledFromConfig()
{
var services = new ServiceCollection();
var config = new ConfigurationBuilder().AddInMemoryCollection([
new("Authentication:Schemes:Bearer:Authority", "https://localhost:5001"),
new("Authentication:Schemes:Bearer:ValidateIssuer", "false"),
new("Authentication:Schemes:Bearer:ValidateAudience", "false"),
]).Build();
services.AddSingleton<IConfiguration>(config);

// Act
RegisterAuth(services.AddAuthentication());
var sp = services.BuildServiceProvider();

// Assert
var jwtBearerOptions = sp.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>().Get(JwtBearerDefaults.AuthenticationScheme);
Assert.Equal("https://localhost:5001", jwtBearerOptions.Authority);
Assert.False(jwtBearerOptions.TokenValidationParameters.ValidateIssuer);
Assert.False(jwtBearerOptions.TokenValidationParameters.ValidateAudience);
}

class InvalidTokenValidator : TokenHandler
{
public InvalidTokenValidator()
Expand Down

0 comments on commit f3db10e

Please sign in to comment.