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

View File

@ -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; }
}
}

View File

@ -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);
}
}
}

View File

@ -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}")
});
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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)
{
}
}
}

View File

@ -8,6 +8,7 @@
UnableToFindDownstreamRouteError,
CannotAddDataError,
CannotFindDataError,
UnableToCompleteRequestError
UnableToCompleteRequestError,
UnableToCreateAuthenticationHandlerError
}
}

View File

@ -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);

View File

@ -43,7 +43,6 @@ namespace Ocelot.Library.Infrastructure.Middleware
{
await _responder.CreateErrorResponse(context, 500);
}
}
else
{

View File

@ -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)

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": {

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>();