380 lines
12 KiB
C#
380 lines
12 KiB
C#
namespace FiscalOS.Infra.Tests.Unit;
|
|
|
|
public class JwtOptionsTests
|
|
{
|
|
[Fact]
|
|
public void JwtOptions_WhenCreatedWithDefaults_ItShouldHaveCorrectDefaultValues()
|
|
{
|
|
var options = new JwtOptions();
|
|
|
|
options.Issuer.Should().Be(string.Empty);
|
|
options.Audience.Should().Be(string.Empty);
|
|
options.Secret.Should().Be(string.Empty);
|
|
options.ExpiryInMinutes.Should().Be(5);
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptions_WhenInitializedWithValues_ItShouldHaveCorrectValues()
|
|
{
|
|
var issuer = "test-issuer";
|
|
var audience = "test-audience";
|
|
var secret = "test-secret-key";
|
|
var expiryInMinutes = 30;
|
|
|
|
var options = new JwtOptions
|
|
{
|
|
Issuer = issuer,
|
|
Audience = audience,
|
|
Secret = secret,
|
|
ExpiryInMinutes = expiryInMinutes
|
|
};
|
|
|
|
options.Issuer.Should().Be(issuer);
|
|
options.Audience.Should().Be(audience);
|
|
options.Secret.Should().Be(secret);
|
|
options.ExpiryInMinutes.Should().Be(expiryInMinutes);
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptions_WhenSecretIsSet_ItShouldReturnSymmetricSecurityKey()
|
|
{
|
|
var secret = "my-super-secret-key-12345";
|
|
var options = new JwtOptions { Secret = secret };
|
|
|
|
var result = options.Key;
|
|
|
|
result.Should().NotBeNull();
|
|
result.Should().BeOfType<SymmetricSecurityKey>();
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptions_WhenSecretIsSet_ItShouldEncodeSecretAsUtf8Bytes()
|
|
{
|
|
var secret = "test-secret";
|
|
var options = new JwtOptions { Secret = secret };
|
|
|
|
var result = options.Key;
|
|
var expectedKey = Encoding.UTF8.GetBytes(secret);
|
|
|
|
result.Key.Should().BeEquivalentTo(expectedKey);
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptions_WhenSecretChanges_ItShouldReturnUpdatedSymmetricSecurityKey()
|
|
{
|
|
var initialSecret = "initial-secret";
|
|
var newSecret = "new-secret";
|
|
var options = new JwtOptions { Secret = initialSecret };
|
|
|
|
var initialKey = options.Key;
|
|
var updatedOptions = options with { Secret = newSecret };
|
|
var updatedKey = updatedOptions.Key;
|
|
|
|
initialKey.Key.Should().NotBeEquivalentTo(updatedKey.Key);
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptions_WhenUsedAsRecord_ItShouldSupportEquality()
|
|
{
|
|
var options1 = new JwtOptions
|
|
{
|
|
Issuer = "issuer",
|
|
Audience = "audience",
|
|
Secret = "secret",
|
|
ExpiryInMinutes = 30
|
|
};
|
|
|
|
var options2 = new JwtOptions
|
|
{
|
|
Issuer = "issuer",
|
|
Audience = "audience",
|
|
Secret = "secret",
|
|
ExpiryInMinutes = 30
|
|
};
|
|
|
|
options1.Should().Be(options2);
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptions_WhenCreatedWithDifferentValues_ItShouldNotBeEqual()
|
|
{
|
|
var options1 = new JwtOptions { Secret = "secret1" };
|
|
var options2 = new JwtOptions { Secret = "secret2" };
|
|
|
|
options1.Should().NotBe(options2);
|
|
}
|
|
|
|
[Fact]
|
|
public void JwtOptionsSetup_WhenConstructed_ItShouldStoreConfiguration()
|
|
{
|
|
var configuration = new ConfigurationBuilder().Build();
|
|
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
|
|
setup.Should().NotBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithValidConfiguration_ItShouldBindJwtOptionsCorrectly()
|
|
{
|
|
var configBuilder = new ConfigurationBuilder();
|
|
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
{ "JwtOptions:Issuer", "test-issuer" },
|
|
{ "JwtOptions:Audience", "test-audience" },
|
|
{ "JwtOptions:Secret", "test-secret" },
|
|
{ "JwtOptions:ExpiryInMinutes", "60" }
|
|
});
|
|
var configuration = configBuilder.Build();
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
var options = new JwtOptions();
|
|
|
|
setup.Configure(options);
|
|
|
|
options.Issuer.Should().Be("test-issuer");
|
|
options.Audience.Should().Be("test-audience");
|
|
options.Secret.Should().Be("test-secret");
|
|
options.ExpiryInMinutes.Should().Be(60);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithPartialConfiguration_ItShouldBindAvailableValuesAndKeepDefaults()
|
|
{
|
|
var configBuilder = new ConfigurationBuilder();
|
|
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
{ "JwtOptions:Issuer", "test-issuer" }
|
|
});
|
|
var configuration = configBuilder.Build();
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
var options = new JwtOptions();
|
|
|
|
setup.Configure(options);
|
|
|
|
options.Issuer.Should().Be("test-issuer");
|
|
options.Audience.Should().Be(string.Empty);
|
|
options.Secret.Should().Be(string.Empty);
|
|
options.ExpiryInMinutes.Should().Be(5);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithMissingSection_ItShouldLeaveOptionsUnchanged()
|
|
{
|
|
var configuration = new ConfigurationBuilder().Build();
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
var options = new JwtOptions();
|
|
|
|
setup.Configure(options);
|
|
|
|
options.Issuer.Should().Be(string.Empty);
|
|
options.Audience.Should().Be(string.Empty);
|
|
options.Secret.Should().Be(string.Empty);
|
|
options.ExpiryInMinutes.Should().Be(5);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithEmptyStringValues_ItShouldBindEmptyStrings()
|
|
{
|
|
var configBuilder = new ConfigurationBuilder();
|
|
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
{ "JwtOptions:Issuer", "" },
|
|
{ "JwtOptions:Audience", "" },
|
|
{ "JwtOptions:Secret", "" }
|
|
});
|
|
var configuration = configBuilder.Build();
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
var options = new JwtOptions();
|
|
|
|
setup.Configure(options);
|
|
|
|
options.Issuer.Should().Be(string.Empty);
|
|
options.Audience.Should().Be(string.Empty);
|
|
options.Secret.Should().Be(string.Empty);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithNumericString_ItShouldBindExpiryInMinutesAsInteger()
|
|
{
|
|
var configBuilder = new ConfigurationBuilder();
|
|
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
{ "JwtOptions:ExpiryInMinutes", "120" }
|
|
});
|
|
var configuration = configBuilder.Build();
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
var options = new JwtOptions();
|
|
|
|
setup.Configure(options);
|
|
|
|
options.ExpiryInMinutes.Should().Be(120);
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledMultipleTimes_ItShouldUpdateOptionsEachTime()
|
|
{
|
|
var configBuilder = new ConfigurationBuilder();
|
|
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
{ "JwtOptions:Issuer", "issuer-1" }
|
|
});
|
|
var configuration = configBuilder.Build();
|
|
var setup = new JwtOptionsSetup(configuration);
|
|
var options = new JwtOptions();
|
|
|
|
setup.Configure(options);
|
|
options.Issuer.Should().Be("issuer-1");
|
|
|
|
var configBuilder2 = new ConfigurationBuilder();
|
|
configBuilder2.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
{ "JwtOptions:Issuer", "issuer-2" }
|
|
});
|
|
var configuration2 = configBuilder2.Build();
|
|
var setup2 = new JwtOptionsSetup(configuration2);
|
|
|
|
setup2.Configure(options);
|
|
options.Issuer.Should().Be("issuer-2");
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithDefaultScheme_ItShouldSetupJwtBearerOptionsWithValidation()
|
|
{
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = "test-secret-key-for-jwt-validation"
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
var bearerOptions = new JwtBearerOptions();
|
|
|
|
setup.Configure(JwtBearerDefaults.AuthenticationScheme, bearerOptions);
|
|
|
|
bearerOptions.TokenValidationParameters.Should().NotBeNull();
|
|
bearerOptions.TokenValidationParameters!.ValidIssuer.Should().Be("test-issuer");
|
|
bearerOptions.TokenValidationParameters.ValidAudience.Should().Be("test-audience");
|
|
bearerOptions.TokenValidationParameters.ValidateIssuer.Should().BeTrue();
|
|
bearerOptions.TokenValidationParameters.ValidateAudience.Should().BeTrue();
|
|
bearerOptions.TokenValidationParameters.ValidateIssuerSigningKey.Should().BeTrue();
|
|
bearerOptions.TokenValidationParameters.ValidateLifetime.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithAllowExpiredTokensScheme_ItShouldDisableLifetimeValidation()
|
|
{
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = "test-secret-key-for-jwt-validation"
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
var bearerOptions = new JwtBearerOptions();
|
|
|
|
setup.Configure(Schemes.AllowExpiredTokens, bearerOptions);
|
|
|
|
bearerOptions.TokenValidationParameters.Should().NotBeNull();
|
|
bearerOptions.TokenValidationParameters!.ValidateLifetime.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithNullSchemeName_ItShouldSetupWithValidation()
|
|
{
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = "test-secret-key-for-jwt-validation"
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
var bearerOptions = new JwtBearerOptions();
|
|
|
|
setup.Configure(null, bearerOptions);
|
|
|
|
bearerOptions.TokenValidationParameters.Should().NotBeNull();
|
|
bearerOptions.TokenValidationParameters!.ValidateLifetime.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithoutNameParameter_ItShouldSetupWithValidation()
|
|
{
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = "test-secret-key-for-jwt-validation"
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
var bearerOptions = new JwtBearerOptions();
|
|
|
|
setup.Configure(bearerOptions);
|
|
|
|
bearerOptions.TokenValidationParameters.Should().NotBeNull();
|
|
bearerOptions.TokenValidationParameters!.ValidateLifetime.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithDifferentSchemeNames_ItShouldOnlyDisableLifetimeForAllowExpiredTokens()
|
|
{
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = "test-secret-key-for-jwt-validation"
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
|
|
var defaultBearerOptions = new JwtBearerOptions();
|
|
setup.Configure(Schemes.Default, defaultBearerOptions);
|
|
|
|
var allowExpiredBearerOptions = new JwtBearerOptions();
|
|
setup.Configure(Schemes.AllowExpiredTokens, allowExpiredBearerOptions);
|
|
|
|
defaultBearerOptions.TokenValidationParameters!.ValidateLifetime.Should().BeTrue();
|
|
allowExpiredBearerOptions.TokenValidationParameters!.ValidateLifetime.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalledWithCustomSchemeName_ItShouldUseDefaultValidationBehavior()
|
|
{
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = "test-secret-key-for-jwt-validation"
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
var bearerOptions = new JwtBearerOptions();
|
|
|
|
setup.Configure("CustomScheme", bearerOptions);
|
|
|
|
bearerOptions.TokenValidationParameters!.ValidateLifetime.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Configure_WhenCalled_ItShouldSetIssuerSigningKeyFromJwtOptions()
|
|
{
|
|
var secret = "test-secret-key-for-jwt-validation";
|
|
var jwtOptions = new JwtOptions
|
|
{
|
|
Issuer = "test-issuer",
|
|
Audience = "test-audience",
|
|
Secret = secret
|
|
};
|
|
var options = Options.Create(jwtOptions);
|
|
var setup = new JwtBearerOptionsSetup(options);
|
|
var bearerOptions = new JwtBearerOptions();
|
|
|
|
setup.Configure(bearerOptions);
|
|
|
|
var expectedKey = jwtOptions.Key;
|
|
bearerOptions.TokenValidationParameters!.IssuerSigningKey.Should().BeEquivalentTo(expectedKey);
|
|
}
|
|
} |