got identity server access token acceptance test working, created factory for choosing auth handlers, a creator for making the auth handlers, some general refactoring...next step is injecting the config for the auth handler creator in some way or just passing it in

This commit is contained in:
TomPallister
2016-10-15 13:50:43 +01:00
parent 34bac7e0d4
commit 320b442526
16 changed files with 369 additions and 95 deletions
@@ -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; }
}
}
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.Responses;
namespace Ocelot.Library.Infrastructure.Authentication
{
/// <summary>
/// Cannot unit test things in this class due to use of extension methods
/// </summary>
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
{
public Response<RequestDelegate> 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<RequestDelegate>(authenticationNext);
}
}
}
@@ -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<AuthenticationHandler> Get(string provider, IApplicationBuilder app)
{
var handler = _creator.CreateIdentityServerAuthenticationHandler(app);
if (!handler.IsError)
{
return new OkResponse<AuthenticationHandler>(new AuthenticationHandler(provider, handler.Data));
}
return new ErrorResponse<AuthenticationHandler>(new List<Error>
{
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for {provider}")
});
}
}
}
@@ -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<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app);
}
}
@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Builder;
using Ocelot.Library.Infrastructure.Responses;
namespace Ocelot.Library.Infrastructure.Authentication
{
public interface IAuthenticationProviderFactory
{
Response<AuthenticationHandler> Get(string provider, IApplicationBuilder app);
}
}
@@ -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)
{
}
}
}
@@ -8,6 +8,7 @@
UnableToFindDownstreamRouteError,
CannotAddDataError,
CannotFindDataError,
UnableToCompleteRequestError
UnableToCompleteRequestError,
UnableToCreateAuthenticationHandlerError
}
}
@@ -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);
@@ -43,7 +43,6 @@ namespace Ocelot.Library.Infrastructure.Middleware
{
await _responder.CreateErrorResponse(context, 500);
}
}
else
{
@@ -3,7 +3,11 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Ocelot.Library.Infrastructure.Responder
{
{
/// <summary>
/// Cannot unit test things in this class due to methods not being implemented
/// on .net concretes used for testing
/// </summary>
public class HttpContextResponder : IHttpResponder
{
public async Task<HttpContext> CreateResponse(HttpContext context, HttpResponseMessage response)
+28 -27
View File
@@ -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": {
+5 -1
View File
@@ -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<YamlConfiguration>(Configuration);
services.AddAuthentication();
@@ -55,6 +57,8 @@ namespace Ocelot
services.AddSingleton<IHttpResponder, HttpContextResponder>();
services.AddSingleton<IRequestBuilder, HttpRequestBuilder>();
services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
services.AddSingleton<IAuthenticationProviderFactory, AuthenticationProviderFactory>();
services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
// 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<IHttpContextAccessor, HttpContextAccessor>();