public async Task AuthorizedRoute_WithCertificateAuthentication_ShouldFailWithUnauthorized_WhenClientCertificateSubjectNameDoesntMatch() { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithSubject("unrecognized-subject-name")) { var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.SecretProvider, SubjectKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(SubjectKey, "CN=subject")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CertificateAuthenticationOnMethodController.AuthorizedGetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Arrange Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task CertificateAuthorizedRoute_WithBypassAttribute_SkipsAuthentication(string route) { // Arrange const string issuerKey = "issuer"; using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName("issuer", "subject")) { var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=issuer")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(route); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } } }
public async Task AuthorizedRoute_WithCertificateAuthentication_ShouldFailOnInvalidBase64Format() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .Build()); services.AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(NoneAuthenticationController.GetRoute) .WithHeader("X-ARR-ClientCert", "something not even close to an client certificate export"); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationViaConfiguration_ShouldFailWithUnauthorized_WhenAnyClientCertificateValidationDoesntSucceeds( string subjectName, string issuerName, string thumbprintNoise, bool expected) { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName(issuerName, subjectName)) { var options = new TestApiServerOptions() .ConfigureAppConfiguration(config => config.AddInMemoryCollection(new [] { new KeyValuePair <string, string>(SubjectKey, "CN=subject"), new KeyValuePair <string, string>(IssuerKey, "CN=issuer"), new KeyValuePair <string, string>(ThumbprintKey, clientCertificate.Thumbprint + thumbprintNoise) })) .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, SubjectKey) .WithIssuer(X509ValidationLocation.Configuration, IssuerKey) .WithThumbprint(X509ValidationLocation.Configuration, ThumbprintKey) .Build()); services.AddSingleton(certificateValidator) .AddClientCertificate(clientCertificate); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CertificateAuthenticationOnMethodController.AuthorizedGetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.True( (HttpStatusCode.Unauthorized == response.StatusCode) == expected, $"Response HTTP status code {(expected ? "should" : "shouldn't")} be 'Unauthorized' but was '{response.StatusCode}'"); } } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationViaConfigurationAndSecretProvider_ShouldFailWithUnauthorized_WhenAnyClientCertificateValidationDoesntSucceeds( string subjectValue, string issuerValue, bool expected) { // Arrange const string subjectKey = "subject", issuerKey = "issuer"; using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName(issuerValue, subjectValue)) { var options = new TestApiServerOptions() .ConfigureAppConfiguration(config => config.AddInMemoryCollection(new [] { new KeyValuePair <string, string>(subjectKey, "CN=known-subject") })) .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, subjectKey) .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=known-issuername")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(NoneAuthenticationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.True( (HttpStatusCode.Unauthorized == response.StatusCode) == expected, $"Response HTTP status code {(expected ? "should" : "shouldn't")} be 'Unauthorized' but was '{response.StatusCode}'"); } } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationInHeader_ShouldSucceed() { // Arrange const string subjectKey = "subject", issuerKey = "issuer"; using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName("known-issuername", "known-subject")) { var options = new TestApiServerOptions() .ConfigureAppConfiguration(config => config.AddInMemoryCollection(new [] { new KeyValuePair <string, string>(subjectKey, "CN=known-subject") })) .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, subjectKey) .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=known-issuername")) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { string base64String = Convert.ToBase64String(clientCertificate.Export(X509ContentType.Pkcs12), Base64FormattingOptions.None); var request = HttpRequestBuilder .Get(NoneAuthenticationController.GetRoute) .WithHeader("X-ARR-ClientCert", base64String); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.NotEqual(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task SharedAccessKeyAuthorizedRoute_EmitsSecurityEventsWhenRequested_RunsAuthentication(bool emitsSecurityEvents) { // Arrange const string issuerKey = "issuer"; var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=issuer")) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication(authOptions => { authOptions.EmitSecurityEvents = emitsSecurityEvents; })); }) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(NoneAuthenticationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); IEnumerable <LogEvent> logEvents = spySink.DequeueLogEvents(); Assert.True(emitsSecurityEvents == logEvents.Any(logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); })); } } }
public async Task CertificateAuthorizedRoute_EmitsSecurityEventsWhenRequested_RunsAuthentication() { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithSubject("unrecognized-subject-name")) { var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.SecretProvider, SubjectKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(SubjectKey, "CN=subject")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator); }) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CertificateAuthenticationOnMethodController.AuthorizedGetRouteEmitSecurityEvents); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); IEnumerable <LogEvent> logEvents = spySink.DequeueLogEvents(); Assert.Contains(logEvents, logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); }); } } } }
public async Task AuthorizedRoute_WithCertificateAuthentication_ShouldFailWithUnauthorized_WhenClientCertificateThumbprintDoesntMatch( string thumbprintNoise, bool expected) { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.Create()) { const string thumbprintKey = "thumbprint"; var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithThumbprint(X509ValidationLocation.SecretProvider, thumbprintKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(thumbprintKey, clientCertificate.Thumbprint + thumbprintNoise)) .AddSingleton(certificateValidator) .AddClientCertificate(clientCertificate) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(NoneAuthenticationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.True( (HttpStatusCode.Unauthorized == response.StatusCode) == expected, $"Response HTTP status code {(expected ? "should" : "shouldn't")} be 'Unauthorized' but was '{response.StatusCode}'"); } } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationOnAppServiceHeader_ShouldSucceeds_WhenClientCertificateSubjectNameMatches(string actualSubject) { // Arrange const string expectedSubject = "known-subject"; using (var cert = SelfSignedCertificate.CreateWithSubject(expectedSubject)) { var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.SecretProvider, SubjectKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(SubjectKey, $"CN={actualSubject}")) .AddSingleton(certificateValidator); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { string clientCertificate = Convert.ToBase64String(cert.RawData); var request = HttpRequestBuilder .Get(CertificateAuthenticationOnMethodController.AuthorizedGetRoute) .WithHeader("X-ARR-ClientCert", clientCertificate); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Arrange bool equalSubject = expectedSubject == actualSubject; bool isUnauthorized = response.StatusCode == HttpStatusCode.Unauthorized; Assert.True(equalSubject != isUnauthorized, "Client certificate with the same subject name should result in an OK HTTP status code"); } } } }