diff --git a/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationHandler.cs b/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationHandler.cs
new file mode 100644
index 00000000..3830e33a
--- /dev/null
+++ b/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationHandler.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Http;
+
+namespace Ocelot.Library.Infrastructure.Authentication
+{
+ public class AuthenticationHandler
+ {
+ public AuthenticationHandler(string provider, RequestDelegate handler)
+ {
+ Provider = provider;
+ Handler = handler;
+ }
+
+ public string Provider { get; private set; }
+ public RequestDelegate Handler { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationHandlerCreator.cs b/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationHandlerCreator.cs
new file mode 100644
index 00000000..1428f239
--- /dev/null
+++ b/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationHandlerCreator.cs
@@ -0,0 +1,32 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Ocelot.Library.Infrastructure.Responses;
+
+namespace Ocelot.Library.Infrastructure.Authentication
+{
+ ///
+ /// Cannot unit test things in this class due to use of extension methods
+ ///
+ public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
+ {
+ public Response CreateIdentityServerAuthenticationHandler(IApplicationBuilder app)
+ {
+ var builder = app.New();
+
+ builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
+ {
+ //todo sort these options out
+ Authority = "http://localhost:51888",
+ ScopeName = "api",
+
+ RequireHttpsMetadata = false
+ });
+
+ builder.UseMvc();
+
+ var authenticationNext = builder.Build();
+
+ return new OkResponse(authenticationNext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationProviderFactory.cs b/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationProviderFactory.cs
new file mode 100644
index 00000000..770462ca
--- /dev/null
+++ b/src/Ocelot.Library/Infrastructure/Authentication/AuthenticationProviderFactory.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Builder;
+using Ocelot.Library.Infrastructure.Errors;
+using Ocelot.Library.Infrastructure.Responses;
+
+namespace Ocelot.Library.Infrastructure.Authentication
+{
+ public class AuthenticationProviderFactory : IAuthenticationProviderFactory
+ {
+ private readonly IAuthenticationHandlerCreator _creator;
+
+ public AuthenticationProviderFactory(IAuthenticationHandlerCreator creator)
+ {
+ _creator = creator;
+ }
+
+ public Response Get(string provider, IApplicationBuilder app)
+ {
+ var handler = _creator.CreateIdentityServerAuthenticationHandler(app);
+
+ if (!handler.IsError)
+ {
+ return new OkResponse(new AuthenticationHandler(provider, handler.Data));
+ }
+
+ return new ErrorResponse(new List
+ {
+ new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for {provider}")
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ocelot.Library/Infrastructure/Authentication/IAuthenticationHandlerCreator.cs b/src/Ocelot.Library/Infrastructure/Authentication/IAuthenticationHandlerCreator.cs
new file mode 100644
index 00000000..cb1b2a91
--- /dev/null
+++ b/src/Ocelot.Library/Infrastructure/Authentication/IAuthenticationHandlerCreator.cs
@@ -0,0 +1,11 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Ocelot.Library.Infrastructure.Responses;
+
+namespace Ocelot.Library.Infrastructure.Authentication
+{
+ public interface IAuthenticationHandlerCreator
+ {
+ Response CreateIdentityServerAuthenticationHandler(IApplicationBuilder app);
+ }
+}
diff --git a/src/Ocelot.Library/Infrastructure/Authentication/IAuthenticationProviderFactory.cs b/src/Ocelot.Library/Infrastructure/Authentication/IAuthenticationProviderFactory.cs
new file mode 100644
index 00000000..e9a25a54
--- /dev/null
+++ b/src/Ocelot.Library/Infrastructure/Authentication/IAuthenticationProviderFactory.cs
@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Builder;
+using Ocelot.Library.Infrastructure.Responses;
+
+namespace Ocelot.Library.Infrastructure.Authentication
+{
+ public interface IAuthenticationProviderFactory
+ {
+ Response Get(string provider, IApplicationBuilder app);
+ }
+}
diff --git a/src/Ocelot.Library/Infrastructure/Authentication/UnableToCreateAuthenticationHandlerError.cs b/src/Ocelot.Library/Infrastructure/Authentication/UnableToCreateAuthenticationHandlerError.cs
new file mode 100644
index 00000000..5637ef73
--- /dev/null
+++ b/src/Ocelot.Library/Infrastructure/Authentication/UnableToCreateAuthenticationHandlerError.cs
@@ -0,0 +1,12 @@
+using Ocelot.Library.Infrastructure.Errors;
+
+namespace Ocelot.Library.Infrastructure.Authentication
+{
+ public class UnableToCreateAuthenticationHandlerError : Error
+ {
+ public UnableToCreateAuthenticationHandlerError(string message)
+ : base(message, OcelotErrorCode.UnableToCreateAuthenticationHandlerError)
+ {
+ }
+ }
+}
diff --git a/src/Ocelot.Library/Infrastructure/Errors/OcelotErrorCode.cs b/src/Ocelot.Library/Infrastructure/Errors/OcelotErrorCode.cs
index e746de55..4f7a1fd7 100644
--- a/src/Ocelot.Library/Infrastructure/Errors/OcelotErrorCode.cs
+++ b/src/Ocelot.Library/Infrastructure/Errors/OcelotErrorCode.cs
@@ -8,6 +8,7 @@
UnableToFindDownstreamRouteError,
CannotAddDataError,
CannotFindDataError,
- UnableToCompleteRequestError
+ UnableToCompleteRequestError,
+ UnableToCreateAuthenticationHandlerError
}
}
diff --git a/src/Ocelot.Library/Infrastructure/Middleware/AuthenticationMiddleware.cs b/src/Ocelot.Library/Infrastructure/Middleware/AuthenticationMiddleware.cs
index 4a72d412..be8f54be 100644
--- a/src/Ocelot.Library/Infrastructure/Middleware/AuthenticationMiddleware.cs
+++ b/src/Ocelot.Library/Infrastructure/Middleware/AuthenticationMiddleware.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
@@ -7,6 +8,11 @@ using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Errors;
using Ocelot.Library.Infrastructure.Repository;
using Ocelot.Library.Infrastructure.Responses;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Ocelot.Library.Infrastructure.Authentication;
namespace Ocelot.Library.Infrastructure.Middleware
{
@@ -16,13 +22,15 @@ namespace Ocelot.Library.Infrastructure.Middleware
private RequestDelegate _authenticationNext;
private readonly IScopedRequestDataRepository _scopedRequestDataRepository;
private readonly IApplicationBuilder _app;
+ private readonly IAuthenticationProviderFactory _authProviderFactory;
public AuthenticationMiddleware(RequestDelegate next, IApplicationBuilder app,
- IScopedRequestDataRepository scopedRequestDataRepository)
+ IScopedRequestDataRepository scopedRequestDataRepository, IAuthenticationProviderFactory authProviderFactory)
: base(scopedRequestDataRepository)
{
_next = next;
_scopedRequestDataRepository = scopedRequestDataRepository;
+ _authProviderFactory = authProviderFactory;
_app = app;
}
@@ -38,25 +46,17 @@ namespace Ocelot.Library.Infrastructure.Middleware
if (IsAuthenticatedRoute(downstreamRoute.Data.ReRoute))
{
- //todo - build auth pipeline and then call normal pipeline if all good?
- //create new app builder
- var builder = _app.New();
- //set up any options for the authentication
- var jwtBearerOptions = new JwtBearerOptions
+ var authenticationNext = _authProviderFactory.Get(downstreamRoute.Data.ReRoute.AuthenticationProvider, _app);
+
+ if (!authenticationNext.IsError)
{
- AutomaticAuthenticate = true,
- AutomaticChallenge = true,
- RequireHttpsMetadata = false,
- };
- //set the authentication middleware
- builder.UseJwtBearerAuthentication(jwtBearerOptions);
- //use mvc so we hit the catch all authorised controller
- builder.UseMvc();
- //then build it
- _authenticationNext = builder.Build();
- //then call it
- await _authenticationNext(context);
- //check if the user is authenticated
+ await authenticationNext.Data.Handler.Invoke(context);
+ }
+ else
+ {
+ SetPipelineError(authenticationNext.Errors);
+ }
+
if (context.User.Identity.IsAuthenticated)
{
await _next.Invoke(context);
diff --git a/src/Ocelot.Library/Infrastructure/Middleware/HttpResponderMiddleware.cs b/src/Ocelot.Library/Infrastructure/Middleware/HttpResponderMiddleware.cs
index 3f268a62..0c8fba3c 100644
--- a/src/Ocelot.Library/Infrastructure/Middleware/HttpResponderMiddleware.cs
+++ b/src/Ocelot.Library/Infrastructure/Middleware/HttpResponderMiddleware.cs
@@ -43,7 +43,6 @@ namespace Ocelot.Library.Infrastructure.Middleware
{
await _responder.CreateErrorResponse(context, 500);
}
-
}
else
{
diff --git a/src/Ocelot.Library/Infrastructure/Responder/HttpContextResponder.cs b/src/Ocelot.Library/Infrastructure/Responder/HttpContextResponder.cs
index e1cfa8a1..e5e31fe4 100644
--- a/src/Ocelot.Library/Infrastructure/Responder/HttpContextResponder.cs
+++ b/src/Ocelot.Library/Infrastructure/Responder/HttpContextResponder.cs
@@ -3,7 +3,11 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Ocelot.Library.Infrastructure.Responder
-{
+{
+ ///
+ /// Cannot unit test things in this class due to methods not being implemented
+ /// on .net concretes used for testing
+ ///
public class HttpContextResponder : IHttpResponder
{
public async Task CreateResponse(HttpContext context, HttpResponseMessage response)
diff --git a/src/Ocelot.Library/project.json b/src/Ocelot.Library/project.json
index 24790da9..f57d8566 100644
--- a/src/Ocelot.Library/project.json
+++ b/src/Ocelot.Library/project.json
@@ -1,34 +1,35 @@
{
"version": "1.0.0-*",
- "dependencies": {
- "Microsoft.NETCore.App": {
- "version": "1.0.0",
- "type": "platform"
+ "dependencies": {
+ "Microsoft.NETCore.App": {
+ "version": "1.0.0",
+ "type": "platform"
+ },
+ "Microsoft.AspNetCore.Mvc": "1.0.0",
+ "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
+ "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
+ "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
+ "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
+ "Microsoft.Extensions.Configuration.Json": "1.0.0",
+ "Microsoft.Extensions.Logging": "1.0.0",
+ "Microsoft.Extensions.Logging.Console": "1.0.0",
+ "Microsoft.Extensions.Logging.Debug": "1.0.0",
+ "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
+ "Microsoft.AspNetCore.Http": "1.0.0",
+ "System.Text.RegularExpressions": "4.1.0",
+ "YamlDotNet": "3.9.0",
+ "Microsoft.AspNetCore.Authentication.OAuth": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.Google": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.Facebook": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.Twitter": "1.0.0",
+ "Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.0.0",
+ "Microsoft.AspNetCore.Authentication": "1.0.0",
+ "IdentityServer4.AccessTokenValidation": "1.0.1-rc2"
},
- "Microsoft.AspNetCore.Mvc": "1.0.0",
- "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
- "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
- "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
- "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
- "Microsoft.Extensions.Configuration.Json": "1.0.0",
- "Microsoft.Extensions.Logging": "1.0.0",
- "Microsoft.Extensions.Logging.Console": "1.0.0",
- "Microsoft.Extensions.Logging.Debug": "1.0.0",
- "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
- "Microsoft.AspNetCore.Http": "1.0.0",
- "System.Text.RegularExpressions": "4.1.0",
- "YamlDotNet": "3.9.0",
- "Microsoft.AspNetCore.Authentication.OAuth": "1.0.0",
- "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
- "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0",
- "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
- "Microsoft.AspNetCore.Authentication.Google": "1.0.0",
- "Microsoft.AspNetCore.Authentication.Facebook": "1.0.0",
- "Microsoft.AspNetCore.Authentication.Twitter": "1.0.0",
- "Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.0.0",
- "Microsoft.AspNetCore.Authentication": "1.0.0"
- },
"frameworks": {
"netcoreapp1.4": {
diff --git a/src/Ocelot/Startup.cs b/src/Ocelot/Startup.cs
index 5c9e44e6..35934de3 100644
--- a/src/Ocelot/Startup.cs
+++ b/src/Ocelot/Startup.cs
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Ocelot.Library.Infrastructure.Authentication;
using Ocelot.Library.Infrastructure.Configuration;
using Ocelot.Library.Infrastructure.Configuration.Yaml;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
@@ -41,7 +42,8 @@ namespace Ocelot
{
services.AddOptions();
services.AddMvc();
-
+ services.AddMvcCore().AddAuthorization().AddJsonFormatters();
+
services.Configure(Configuration);
services.AddAuthentication();
@@ -55,6 +57,8 @@ namespace Ocelot
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
services.AddSingleton();
diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs
index 6b7ca01c..bfefdc7f 100644
--- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs
+++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
+using System.Net.Http.Headers;
using System.Text.Encodings.Web;
using IdentityServer4.Models;
using IdentityServer4.Services.InMemory;
@@ -11,6 +12,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
using Ocelot.Library.Infrastructure.Configuration.Yaml;
using Shouldly;
using TestStack.BDDfy;
@@ -21,17 +23,17 @@ namespace Ocelot.AcceptanceTests
{
public class AuthenticationTests : IDisposable
{
- private TestServer _server;
- private HttpClient _client;
+ private TestServer _ocelotServer;
+ private HttpClient _ocelotClient;
private HttpResponseMessage _response;
private readonly string _configurationPath;
private StringContent _postContent;
- private IWebHost _builder;
+ private IWebHost _ocelotBbuilder;
// Sadly we need to change this when we update the netcoreapp version to make the test update the config correctly
private double _netCoreAppVersion = 1.4;
- private HttpClient _idServerClient;
- private TestServer _idServer;
+ private BearerToken _token;
+ private IWebHost _identityServerBuilder;
public AuthenticationTests()
{
@@ -39,7 +41,7 @@ namespace Ocelot.AcceptanceTests
}
[Fact]
- public void should_return_401_using_jwt()
+ public void should_return_401_using_identity_server_access_token()
{
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888"))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
@@ -63,6 +65,33 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
+ [Fact]
+ public void should_return_201_using_identity_server_access_token()
+ {
+ this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888"))
+ .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
+ .And(x => x.GivenIHaveAToken("http://localhost:51888"))
+ .And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
+ {
+ ReRoutes = new List
+ {
+ new YamlReRoute
+ {
+ DownstreamTemplate = "http://localhost:51876/",
+ UpstreamTemplate = "/",
+ UpstreamHttpMethod = "Post",
+ Authentication = "JwtBearerAuthentication"
+ }
+ }
+ }))
+ .And(x => x.GivenTheApiGatewayIsRunning())
+ .And(x => x.GivenIHaveAddedATokenToMyRequest())
+ .And(x => x.GivenThePostHasContent("postContent"))
+ .When(x => x.WhenIPostUrlOnTheApiGateway("/"))
+ .Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
+ .BDDfy();
+ }
+
private void GivenThePostHasContent(string postcontent)
{
_postContent = new StringContent(postcontent);
@@ -73,10 +102,10 @@ namespace Ocelot.AcceptanceTests
///
private void GivenTheApiGatewayIsRunning()
{
- _server = new TestServer(new WebHostBuilder()
+ _ocelotServer = new TestServer(new WebHostBuilder()
.UseStartup());
- _client = _server.CreateClient();
+ _ocelotClient = _ocelotServer.CreateClient();
}
private void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
@@ -96,7 +125,7 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
{
- _builder = new WebHostBuilder()
+ _ocelotBbuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
@@ -112,12 +141,12 @@ namespace Ocelot.AcceptanceTests
})
.Build();
- _builder.Start();
+ _ocelotBbuilder.Start();
}
private void GivenThereIsAnIdentityServerOn(string url)
{
- var builder = new WebHostBuilder()
+ _identityServerBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
@@ -126,41 +155,51 @@ namespace Ocelot.AcceptanceTests
.ConfigureServices(services =>
{
services.AddDeveloperIdentityServer()
- .AddInMemoryClients(new List {
- new Client
- {
- ClientId = "test",
- AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
- ClientSecrets = new List { new Secret("test".Sha256()) },
- AllowedScopes = new List { "api1" },
- AllowAccessToAllScopes = true,
- AccessTokenType = AccessTokenType.Jwt,
- Enabled = true
-
- } })
.AddInMemoryScopes(new List { new Scope
{
- Name = "api1",
+ Name = "api",
Description = "My API",
Enabled = true
}})
+ .AddInMemoryClients(new List {
+ new Client
+ {
+ ClientId = "client",
+ AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
+ ClientSecrets = new List { new Secret("secret".Sha256()) },
+ AllowedScopes = new List { "api" },
+ AccessTokenType = AccessTokenType.Jwt,
+ Enabled = true,
+ RequireClientSecret = false
+ } })
.AddInMemoryUsers(new List { new InMemoryUser
{
- Username = "test", Password = "test", Enabled = true, Subject = "asdads"
+ Username = "test",
+ Password = "test",
+ Enabled = true,
+ Subject = "asdads"
}});
})
.Configure(app =>
{
app.UseIdentityServer();
- });
-
- _idServer = new TestServer(builder);
- _idServerClient = _idServer.CreateClient();
+ })
+ .Build();
- var response = _idServerClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
- response.EnsureSuccessStatusCode();
- var content = response.Content.ReadAsStringAsync().Result;
+ _identityServerBuilder.Start();
+
+ VerifyIdentiryServerStarted(url);
+
+ }
+
+ private void VerifyIdentiryServerStarted(string url)
+ {
+ using (var httpClient = new HttpClient())
+ {
+ var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
+ response.EnsureSuccessStatusCode();
+ }
}
private void GivenIHaveAToken(string url)
@@ -168,21 +207,32 @@ namespace Ocelot.AcceptanceTests
var tokenUrl = $"{url}/connect/token";
var formData = new List>
{
- new KeyValuePair("client_id", "test"),
- new KeyValuePair("client_secret", "test".Sha256()),
- new KeyValuePair("scope", "api1"),
+ new KeyValuePair("client_id", "client"),
+ new KeyValuePair("client_secret", "secret"),
+ new KeyValuePair("scope", "api"),
new KeyValuePair("username", "test"),
new KeyValuePair("password", "test"),
new KeyValuePair("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
- var response = _idServerClient.PostAsync(tokenUrl, content).Result;
- var responseContent = response.Content.ReadAsStringAsync().Result;
+
+ using (var httpClient = new HttpClient())
+ {
+ var response = httpClient.PostAsync(tokenUrl, content).Result;
+ response.EnsureSuccessStatusCode();
+ var responseContent = response.Content.ReadAsStringAsync().Result;
+ _token = JsonConvert.DeserializeObject(responseContent);
+ }
+ }
+
+ private void GivenIHaveAddedATokenToMyRequest()
+ {
+ _ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
}
private void WhenIPostUrlOnTheApiGateway(string url)
{
- _response = _client.PostAsync(url, _postContent).Result;
+ _response = _ocelotClient.PostAsync(url, _postContent).Result;
}
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
@@ -192,11 +242,22 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
- _idServerClient?.Dispose();
- _idServer?.Dispose();
- _builder?.Dispose();
- _client.Dispose();
- _server.Dispose();
+ _ocelotBbuilder?.Dispose();
+ _ocelotClient?.Dispose();
+ _ocelotServer?.Dispose();
+ _identityServerBuilder?.Dispose();
+ }
+
+ class BearerToken
+ {
+ [JsonProperty("access_token")]
+ public string AccessToken { get; set; }
+
+ [JsonProperty("expires_in")]
+ public int ExpiresIn { get; set; }
+
+ [JsonProperty("token_type")]
+ public string TokenType { get; set; }
}
}
}
diff --git a/test/Ocelot.AcceptanceTests/configuration.yaml b/test/Ocelot.AcceptanceTests/configuration.yaml
index 42e7ab07..c438428b 100644
--- a/test/Ocelot.AcceptanceTests/configuration.yaml
+++ b/test/Ocelot.AcceptanceTests/configuration.yaml
@@ -2,4 +2,4 @@ ReRoutes:
- DownstreamTemplate: http://localhost:51879/
UpstreamTemplate: /
UpstreamHttpMethod: Get
- Authentication: IdentityServer
+ Authentication: IdentityServer.AccessToken
diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationProviderFactoryTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationProviderFactoryTests.cs
new file mode 100644
index 00000000..752aca0b
--- /dev/null
+++ b/test/Ocelot.UnitTests/Authentication/AuthenticationProviderFactoryTests.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Moq;
+using Ocelot.Library.Infrastructure.Authentication;
+using Ocelot.Library.Infrastructure.Errors;
+using Ocelot.Library.Infrastructure.Responses;
+using Shouldly;
+using TestStack.BDDfy;
+using Xunit;
+
+namespace Ocelot.UnitTests.Authentication
+{
+ public class AuthenticationProviderFactoryTests
+ {
+ private readonly IAuthenticationProviderFactory _authenticationProviderFactory;
+ private readonly Mock _app;
+ private readonly Mock _creator;
+
+ private string _provider;
+ private Response _result;
+
+ public AuthenticationProviderFactoryTests()
+ {
+ _app = new Mock();
+ _creator = new Mock();
+ _authenticationProviderFactory = new AuthenticationProviderFactory(_creator.Object);
+ }
+
+ [Fact]
+ public void should_return_identity_server_access_token_provider()
+ {
+ this.Given(x => x.GivenTheProviderIs("IdentityServer.AccessToken"))
+ .And(x => x.GivenTheCreatorReturns())
+ .When(x => x.WhenIGetFromTheFactory())
+ .Then(x => x.ThenTheHandlerIsReturned("IdentityServer.AccessToken"))
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_error_if_cannot_create_handler()
+ {
+ this.Given(x => x.GivenTheProviderIs("IdentityServer.AccessToken"))
+ .And(x => x.GivenTheCreatorReturnsAnError())
+ .When(x => x.WhenIGetFromTheFactory())
+ .Then(x => x.ThenAnErrorResponseIsReturned())
+ .BDDfy();
+ }
+
+ private void GivenTheCreatorReturnsAnError()
+ {
+ _creator
+ .Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny()))
+ .Returns(new ErrorResponse(new List
+ {
+ new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx")
+ }));
+ }
+
+ private void GivenTheCreatorReturns()
+ {
+ _creator
+ .Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny()))
+ .Returns(new OkResponse(x => Task.CompletedTask));
+ }
+
+ private void GivenTheProviderIs(string provider)
+ {
+ _provider = provider;
+ }
+
+ private void WhenIGetFromTheFactory()
+ {
+ _result = _authenticationProviderFactory.Get(_provider, _app.Object);
+ }
+
+ private void ThenTheHandlerIsReturned(string expected)
+ {
+ _result.Data.Provider.ShouldBe(expected);
+ }
+
+ private void ThenAnErrorResponseIsReturned()
+ {
+ _result.IsError.ShouldBeTrue();
+ }
+ }
+}
diff --git a/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs
index fb4e2d32..6c82b0fe 100644
--- a/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs
+++ b/test/Ocelot.UnitTests/Middleware/AuthenticationMiddlewareTests.cs
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
+using Ocelot.Library.Infrastructure.Authentication;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Middleware;
using Ocelot.Library.Infrastructure.Repository;
@@ -21,6 +22,7 @@ namespace Ocelot.UnitTests.Middleware
public class AuthenticationMiddlewareTests : IDisposable
{
private readonly Mock _scopedRepository;
+ private readonly Mock _authFactory;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
@@ -31,10 +33,11 @@ namespace Ocelot.UnitTests.Middleware
{
_url = "http://localhost:51879";
_scopedRepository = new Mock();
-
+ _authFactory = new Mock();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
+ x.AddSingleton(_authFactory.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)