diff --git a/build/dependencies.props b/build/dependencies.props
index 0d2aabf..36d926a 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -27,7 +27,7 @@
http://github.com/xabaril/Acheve.TestHost
Xabaril Contributors
Xabaril
- 1.4.2
+ 2.0.0
Achve.TestHost is a nuget package to improve TestServer experiences.
For more information see http://github.com/Xabaril/Acheve.TestHost
TestHost;TestServer
diff --git a/samples/Sample.IntegrationTests/Specs/ValuesTests.cs b/samples/Sample.IntegrationTests/Specs/ValuesTests.cs
index 99c6e9a..6843580 100644
--- a/samples/Sample.IntegrationTests/Specs/ValuesTests.cs
+++ b/samples/Sample.IntegrationTests/Specs/ValuesTests.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using System.Linq;
+using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
@@ -20,9 +21,8 @@ public VauesWithDefaultUserTests(TestHostFixture fixture)
}
[Fact]
- public async Task WithRequestBuilder()
+ public async Task Authorized_User_Should_Get_200()
{
- // Or you can create a request and assign the identity to the RequestBuilder
var response = await _fixture.Server.CreateHttpApiRequest(controller=>controller.Values())
.WithIdentity(Identities.User)
.GetAsync();
@@ -31,20 +31,20 @@ public async Task WithRequestBuilder()
}
[Fact]
- public async Task WithEmptyRequestBuilder()
+ public async Task User_With_No_Claims_Is_Unauthorized()
{
- // Or you can create a request and assign the identity to the RequestBuilder
var response = await _fixture.Server.CreateHttpApiRequest(controller => controller.Values())
.WithIdentity(Identities.Empty)
.GetAsync();
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
+ response.Headers.WwwAuthenticate.Count.Should().Be(1);
+ response.Headers.WwwAuthenticate.First().Scheme.Should().Be("TestServer");
}
[Fact]
- public async Task WithRequestBuilderAndSpecificScheme()
+ public async Task Authorized_User_Should_Get_200_Using_A_Specific_Scheme()
{
- // Or you can create a request and assign the identity to the RequestBuilder
var response = await _fixture.Server.CreateHttpApiRequest(controller => controller.ValuesWithSchema())
.WithIdentity(Identities.User, "Bearer")
.GetAsync();
@@ -55,16 +55,17 @@ public async Task WithRequestBuilderAndSpecificScheme()
[Fact]
public async Task WithRequestBuilderAndSpecificSchemeUnauthorized()
{
- // Or you can create a request and assign the identity to the RequestBuilder
var response = await _fixture.Server.CreateHttpApiRequest(controller => controller.ValuesWithSchema())
- .WithIdentity(Identities.User)
+ .WithIdentity(Identities.User) // We are not using the expected "Bearer" schema
.GetAsync();
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
+ response.Headers.WwwAuthenticate.Count.Should().Be(1);
+ response.Headers.WwwAuthenticate.First().Scheme.Should().Be("Bearer");
}
[Fact]
- public async Task Anonymous()
+ public async Task Authentication_Is_Not_Performed_For_Non_Protected_Endpoints()
{
var response = await _fixture.Server.CreateHttpApiRequest(controller => controller.PublicValues())
.GetAsync();
diff --git a/samples/Sample.IntegrationTests/TestStartup.cs b/samples/Sample.IntegrationTests/TestStartup.cs
index a983619..9d21755 100644
--- a/samples/Sample.IntegrationTests/TestStartup.cs
+++ b/samples/Sample.IntegrationTests/TestStartup.cs
@@ -1,6 +1,5 @@
-using System;
-using System.Reflection;
-using System.Security.Claims;
+using System.Reflection;
+using System.Threading.Tasks;
using Acheve.AspNetCore.TestHost.Security;
using Acheve.TestHost;
using Microsoft.AspNetCore.Builder;
@@ -14,12 +13,28 @@ public class TestStartup
{
public void ConfigureServices(IServiceCollection services)
{
- services.AddAuthentication(TestServerAuthenticationDefaults.AuthenticationScheme)
- .AddTestServerAuthentication()
- .AddTestServerAuthentication("Bearer", options =>
+ services.AddAuthentication(TestServerDefaults.AuthenticationScheme)
+ .AddTestServer(options =>
+ {
+ options.Events = new TestServerEvents
+ {
+ OnMessageReceived = context => Task.CompletedTask,
+ OnTokenValidated = context => Task.CompletedTask,
+ OnAuthenticationFailed = context => Task.CompletedTask,
+ OnChallenge = context => Task.CompletedTask
+ };
+ })
+ .AddTestServer("Bearer", options =>
{
options.NameClaimType = "name";
options.RoleClaimType = "role";
+ options.Events = new TestServerEvents
+ {
+ OnMessageReceived = context => Task.CompletedTask,
+ OnTokenValidated = context => Task.CompletedTask,
+ OnAuthenticationFailed = context => Task.CompletedTask,
+ OnChallenge = context => Task.CompletedTask
+ };
});
var mvcCoreBuilder = services.AddMvcCore()
diff --git a/src/Acheve.TestHost/AuthenticationBuilderExtensions.cs b/src/Acheve.TestHost/AuthenticationBuilderExtensions.cs
index 5496e12..d81558b 100644
--- a/src/Acheve.TestHost/AuthenticationBuilderExtensions.cs
+++ b/src/Acheve.TestHost/AuthenticationBuilderExtensions.cs
@@ -6,44 +6,44 @@ namespace Acheve.AspNetCore.TestHost.Security
{
public static class AuthenticationBuilderExtensions
{
- public static AuthenticationBuilder AddTestServerAuthentication(this AuthenticationBuilder builder)
- => builder.AddTestServerAuthentication(
- authenticationScheme: TestServerAuthenticationDefaults.AuthenticationScheme,
+ public static AuthenticationBuilder AddTestServer(this AuthenticationBuilder builder)
+ => builder.AddTestServer(
+ authenticationScheme: TestServerDefaults.AuthenticationScheme,
displayName: null,
configureOptions: _ => { });
- public static AuthenticationBuilder AddTestServerAuthentication(
+ public static AuthenticationBuilder AddTestServer(
this AuthenticationBuilder builder,
string authenticationScheme)
- => builder.AddTestServerAuthentication(
+ => builder.AddTestServer(
authenticationScheme: authenticationScheme,
displayName: null,
configureOptions: _ => { });
- public static AuthenticationBuilder AddTestServerAuthentication(
+ public static AuthenticationBuilder AddTestServer(
this AuthenticationBuilder builder,
- Action configureOptions)
- => builder.AddTestServerAuthentication(
- authenticationScheme: TestServerAuthenticationDefaults.AuthenticationScheme,
+ Action configureOptions)
+ => builder.AddTestServer(
+ authenticationScheme: TestServerDefaults.AuthenticationScheme,
displayName: null,
configureOptions: configureOptions);
- public static AuthenticationBuilder AddTestServerAuthentication(
+ public static AuthenticationBuilder AddTestServer(
this AuthenticationBuilder builder,
string authenticationScheme,
- Action configureOptions)
- => builder.AddTestServerAuthentication(
+ Action configureOptions)
+ => builder.AddTestServer(
authenticationScheme: authenticationScheme,
displayName: null,
configureOptions: configureOptions);
- public static AuthenticationBuilder AddTestServerAuthentication(
+ public static AuthenticationBuilder AddTestServer(
this AuthenticationBuilder builder,
string authenticationScheme,
string displayName,
- Action configureOptions)
+ Action configureOptions)
{
- return builder.AddScheme(
+ return builder.AddScheme(
authenticationScheme: authenticationScheme,
displayName: displayName,
configureOptions: configureOptions);
diff --git a/src/Acheve.TestHost/HttpClientExtensions.cs b/src/Acheve.TestHost/HttpClientExtensions.cs
index 447ad92..b702684 100644
--- a/src/Acheve.TestHost/HttpClientExtensions.cs
+++ b/src/Acheve.TestHost/HttpClientExtensions.cs
@@ -17,11 +17,11 @@ public static class HttpClientExtensions
public static HttpClient WithDefaultIdentity(this HttpClient httpClient, IEnumerable claims)
{
var headerName =
- AuthenticationHeaderHelper.GetHeaderName(TestServerAuthenticationDefaults.AuthenticationScheme);
+ AuthenticationHeaderHelper.GetHeaderName(TestServerDefaults.AuthenticationScheme);
httpClient.DefaultRequestHeaders.Add(
name: headerName,
- value: $"{TestServerAuthenticationDefaults.AuthenticationScheme} {DefautClaimsEncoder.Encode(claims)}");
+ value: $"{TestServerDefaults.AuthenticationScheme} {DefautClaimsEncoder.Encode(claims)}");
return httpClient;
}
diff --git a/src/Acheve.TestHost/RequestBuilderExtensions.cs b/src/Acheve.TestHost/RequestBuilderExtensions.cs
index f98c719..894c560 100644
--- a/src/Acheve.TestHost/RequestBuilderExtensions.cs
+++ b/src/Acheve.TestHost/RequestBuilderExtensions.cs
@@ -17,11 +17,11 @@ public static class RequestBuilderExtensions
public static RequestBuilder WithIdentity(this RequestBuilder requestBuilder, IEnumerable claims)
{
var headerName =
- AuthenticationHeaderHelper.GetHeaderName(TestServerAuthenticationDefaults.AuthenticationScheme);
+ AuthenticationHeaderHelper.GetHeaderName(TestServerDefaults.AuthenticationScheme);
requestBuilder.AddHeader(
headerName,
- $"{TestServerAuthenticationDefaults.AuthenticationScheme} {DefautClaimsEncoder.Encode(claims)}");
+ $"{TestServerDefaults.AuthenticationScheme} {DefautClaimsEncoder.Encode(claims)}");
return requestBuilder;
}
diff --git a/src/Acheve.TestHost/Security/AuthenticationFailedContext.cs b/src/Acheve.TestHost/Security/AuthenticationFailedContext.cs
new file mode 100644
index 0000000..7a6b788
--- /dev/null
+++ b/src/Acheve.TestHost/Security/AuthenticationFailedContext.cs
@@ -0,0 +1,17 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using System;
+
+namespace Acheve.TestHost
+{
+ public class AuthenticationFailedContext : ResultContext
+ {
+ public AuthenticationFailedContext(
+ HttpContext context,
+ AuthenticationScheme scheme,
+ TestServerOptions options)
+ : base(context, scheme, options) { }
+
+ public Exception Exception { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Acheve.TestHost/Security/MessageReceivedContext.cs b/src/Acheve.TestHost/Security/MessageReceivedContext.cs
new file mode 100644
index 0000000..c664820
--- /dev/null
+++ b/src/Acheve.TestHost/Security/MessageReceivedContext.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+
+namespace Acheve.TestHost
+{
+ public class MessageReceivedContext : ResultContext
+ {
+ public MessageReceivedContext(
+ HttpContext context,
+ AuthenticationScheme scheme,
+ TestServerOptions options)
+ : base(context, scheme, options) { }
+
+ public string Token { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Acheve.TestHost/Security/TestServerAuthenticationHandler.cs b/src/Acheve.TestHost/Security/TestServerAuthenticationHandler.cs
deleted file mode 100644
index 6509458..0000000
--- a/src/Acheve.TestHost/Security/TestServerAuthenticationHandler.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Primitives;
-using System.Linq;
-using System.Net.Http.Headers;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-
-namespace Acheve.TestHost
-{
- public class TestServerAuthenticationHandler : AuthenticationHandler
- {
- public TestServerAuthenticationHandler(
- IOptionsMonitor options,
- ILoggerFactory logger,
- UrlEncoder encoder,
- ISystemClock clock)
- : base(options, logger, encoder, clock)
- {
- }
-
- protected override Task HandleAuthenticateAsync()
- {
- var headerName = AuthenticationHeaderHelper.GetHeaderName(Scheme.Name);
-
- StringValues authHeaderString;
- var existAuthorizationHeader =
- Context.Request.Headers.TryGetValue(headerName, out authHeaderString);
-
- if (existAuthorizationHeader == false)
- {
- Logger.LogInformation("{Scheme} No {HeaderName} header present", Scheme.Name, headerName);
- return Task.FromResult(AuthenticateResult.Fail("No Authorization header present"));
- }
-
- AuthenticationHeaderValue authHeader;
- var canParse = AuthenticationHeaderValue.TryParse(authHeaderString[0], out authHeader);
-
- if (canParse == false)
- {
- Logger.LogInformation("{Scheme} {HeaderName} header not valid", Scheme.Name, headerName);
- return Task.FromResult(AuthenticateResult.Fail("Authorization header not valid"));
- }
-
- var headerClaims = DefautClaimsEncoder.Decode(authHeader.Parameter).ToArray();
-
- if (headerClaims.Length == 0)
- {
- Logger.LogInformation("{Scheme} Invalid claims", Scheme.Name);
- return Task.FromResult(AuthenticateResult.Fail("Invalid claims"));
- }
-
- var identity = new ClaimsIdentity(
- claims: Options.CommonClaims.Union(headerClaims),
- authenticationType: Scheme.Name,
- nameType: Options.NameClaimType,
- roleType: Options.RoleClaimType);
-
- var ticket = new AuthenticationTicket(
- new ClaimsPrincipal(identity),
- new AuthenticationProperties(),
- Scheme.Name);
-
- Logger.LogInformation("{Scheme} Authenticated", Scheme.Name);
- return Task.FromResult(AuthenticateResult.Success(ticket));
- }
- }
-}
diff --git a/src/Acheve.TestHost/Security/TestServerChallengeContext.cs b/src/Acheve.TestHost/Security/TestServerChallengeContext.cs
new file mode 100644
index 0000000..53540c0
--- /dev/null
+++ b/src/Acheve.TestHost/Security/TestServerChallengeContext.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using System;
+
+namespace Acheve.TestHost
+{
+ public class TestServerChallengeContext : PropertiesContext
+ {
+ public TestServerChallengeContext(
+ HttpContext context,
+ AuthenticationScheme scheme,
+ TestServerOptions options,
+ AuthenticationProperties properties)
+ : base(context, scheme, options, properties) { }
+
+ public Exception AuthenticateFailure { get; set; }
+
+ public string Error { get; set; }
+
+ public bool Handled { get; private set; }
+
+ public void HandleResponse() => Handled = true;
+ }
+}
\ No newline at end of file
diff --git a/src/Acheve.TestHost/Security/TestServerAuthenticationDefaults.cs b/src/Acheve.TestHost/Security/TestServerDefaults.cs
similarity index 65%
rename from src/Acheve.TestHost/Security/TestServerAuthenticationDefaults.cs
rename to src/Acheve.TestHost/Security/TestServerDefaults.cs
index 9281210..0ebdd66 100644
--- a/src/Acheve.TestHost/Security/TestServerAuthenticationDefaults.cs
+++ b/src/Acheve.TestHost/Security/TestServerDefaults.cs
@@ -1,6 +1,6 @@
namespace Acheve.TestHost
{
- public static class TestServerAuthenticationDefaults
+ public static class TestServerDefaults
{
public const string AuthenticationScheme = "TestServer";
}
diff --git a/src/Acheve.TestHost/Security/TestServerEvents.cs b/src/Acheve.TestHost/Security/TestServerEvents.cs
new file mode 100644
index 0000000..7c87ca6
--- /dev/null
+++ b/src/Acheve.TestHost/Security/TestServerEvents.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Acheve.TestHost
+{
+ public class TestServerEvents
+ {
+ public Func OnAuthenticationFailed { get; set; } = context => Task.CompletedTask;
+
+ public Func OnMessageReceived { get; set; } = context => Task.CompletedTask;
+
+ public Func OnTokenValidated { get; set; } = context => Task.CompletedTask;
+
+ public Func OnChallenge { get; set; } = context => Task.CompletedTask;
+
+ public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
+
+ public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
+
+ public virtual Task TokenValidated(TokenValidatedContext context) => OnTokenValidated(context);
+
+ public virtual Task Challenge(TestServerChallengeContext context) => OnChallenge(context);
+ }
+}
\ No newline at end of file
diff --git a/src/Acheve.TestHost/Security/TestServerHandler.cs b/src/Acheve.TestHost/Security/TestServerHandler.cs
new file mode 100644
index 0000000..719cbd2
--- /dev/null
+++ b/src/Acheve.TestHost/Security/TestServerHandler.cs
@@ -0,0 +1,144 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Net.Http.Headers;
+using System;
+using System.Linq;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+
+namespace Acheve.TestHost
+{
+ public class TestServerHandler : AuthenticationHandler
+ {
+ public TestServerHandler(
+ IOptionsMonitor options,
+ ILoggerFactory logger,
+ UrlEncoder encoder,
+ ISystemClock clock)
+ : base(options, logger, encoder, clock)
+ {
+ }
+
+ protected new TestServerEvents Events
+ {
+ get => (TestServerEvents)base.Events;
+ set => base.Events = value;
+ }
+
+ protected override Task