diff --git a/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs b/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs index a21036a3..1e0a4f72 100644 --- a/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs +++ b/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs @@ -1,5 +1,6 @@ using System; using IdentityServer4.AccessTokenValidation; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -21,22 +22,22 @@ namespace Ocelot.Authentication.Handler.Creator throw new NotImplementedException(); var builder = app.New(); - if (authOptions.Provider.ToLower() == "jwt") + /* if (authOptions.Provider.ToLower() == "jwt") { var authenticationConfig = authOptions.Config as JwtConfig; - /* builder.UseJwtBearerAuthentication( + builder.UseJwtBearerAuthentication( new JwtBearerOptions() { Authority = authenticationConfig.Authority, Audience = authenticationConfig.Audience - });*/ + }); } else { var authenticationConfig = authOptions.Config as IdentityServerConfig; - /* builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions + builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions { Authority = authenticationConfig.ProviderRootUrl, ApiName = authenticationConfig.ApiName, @@ -44,11 +45,12 @@ namespace Ocelot.Authentication.Handler.Creator AllowedScopes = authOptions.AllowedScopes, SupportedTokens = SupportedTokens.Both, ApiSecret = authenticationConfig.ApiSecret - });*/ - } + }); + }*/ var authenticationNext = builder.Build(); + return new OkResponse(authenticationNext); } } diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs index f6ecd654..1c36d522 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs @@ -1,7 +1,14 @@ using System.Collections.Generic; +using System.Security.Claims; +using System.Text.Encodings.Web; using System.Threading.Tasks; +using IdentityServer4.Extensions; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Ocelot.Authentication.Handler.Factory; using Ocelot.Configuration; using Ocelot.Errors; @@ -34,6 +41,32 @@ namespace Ocelot.Authentication.Middleware public async Task Invoke(HttpContext context) { + /* var req = context.Request; + var res = context.Response; + if (req.Path.StartsWithSegments(new PathString("/add"), out var remainder)) + { + var name = remainder.Value.Substring(1); + var auth = context.RequestServices.GetRequiredService(); + var scheme = new AuthenticationScheme(name, name, typeof(TestHandler)); + auth.AddScheme(scheme); + } + else if (req.Path.StartsWithSegments(new PathString("/auth"), out remainder)) + { + var name = (remainder.Value.Length > 0) ? remainder.Value.Substring(1) : null; + var result = await context.AuthenticateAsync(name); + result.Principal.IsAuthenticated(); + } + else if (req.Path.StartsWithSegments(new PathString("/remove"), out remainder)) + { + var name = remainder.Value.Substring(1); + var auth = context.RequestServices.GetRequiredService(); + auth.RemoveScheme(name); + } + else + { + await _next.Invoke(context); + }*/ + if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) { _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); @@ -81,5 +114,43 @@ namespace Ocelot.Authentication.Middleware return reRoute.IsAuthenticated; } } + + public class TestHandler : AuthenticationHandler + { + public TestHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { + } + + protected override Task HandleAuthenticateAsync() + { + var principal = new ClaimsPrincipal(); + var id = new ClaimsIdentity(); + id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name)); + if (Options.Instance != null) + { + id.AddClaim(new Claim("Count", Options.Instance.Count.ToString())); + } + principal.AddIdentity(id); + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); + } + } + + public class TestOptions : AuthenticationSchemeOptions + { + public Singleton Instance { get; set; } + } + + public class Singleton + { + public static int _count; + + public Singleton() + { + _count++; + Count = _count; + } + + public int Count { get; } + } } diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 95ed7597..dfdb8140 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -38,10 +38,14 @@ using Ocelot.Responder; using Ocelot.ServiceDiscovery; using System; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net.Http; using System.Reflection; using System.Security.Cryptography.X509Certificates; +using IdentityServer4.AccessTokenValidation; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.IdentityModel.Tokens; using Ocelot.Configuration; using Ocelot.Creator.Configuration; @@ -75,7 +79,6 @@ namespace Ocelot.DependencyInjection services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -84,59 +87,7 @@ namespace Ocelot.DependencyInjection services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); - - var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(); - - if(identityServerConfiguration != null) - { - services.TryAddSingleton(identityServerConfiguration); - services.TryAddSingleton(); - var identityServerBuilder = services - .AddIdentityServer(options => { - options.IssuerUri = "Ocelot"; - }) - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = identityServerConfiguration.ApiName, - Description = identityServerConfiguration.Description, - Enabled = identityServerConfiguration.Enabled, - DisplayName = identityServerConfiguration.ApiName, - Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(), - ApiSecrets = new List - { - new Secret - { - Value = identityServerConfiguration.ApiSecret.Sha256() - } - } - } - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = identityServerConfiguration.ApiName, - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret(identityServerConfiguration.ApiSecret.Sha256())}, - AllowedScopes = identityServerConfiguration.AllowedScopes, - AccessTokenType = identityServerConfiguration.AccessTokenType, - Enabled = identityServerConfiguration.Enabled, - RequireClientSecret = identityServerConfiguration.RequireClientSecret - } - }).AddResourceOwnerValidator(); - - if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword)) - { - identityServerBuilder.AddDeveloperSigningCredential(); - } - else - { - var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword); - identityServerBuilder.AddSigningCredential(cert); - } - } + services.TryAddSingleton(); var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly; @@ -190,6 +141,76 @@ namespace Ocelot.DependencyInjection //Used to log the the start and ending of middleware services.TryAddSingleton(); services.AddMiddlewareAnalysis(); + services.AddWebEncoders(); + + var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(); + + if (identityServerConfiguration != null) + { + services.TryAddSingleton(identityServerConfiguration); + services.TryAddSingleton(); + var identityServerBuilder = services + .AddIdentityServer(options => { + options.IssuerUri = "Ocelot"; + }) + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = identityServerConfiguration.ApiName, + Description = identityServerConfiguration.Description, + Enabled = identityServerConfiguration.Enabled, + DisplayName = identityServerConfiguration.ApiName, + Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(), + ApiSecrets = new List + { + new Secret + { + Value = identityServerConfiguration.ApiSecret.Sha256() + } + } + } + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = identityServerConfiguration.ApiName, + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret(identityServerConfiguration.ApiSecret.Sha256())}, + AllowedScopes = identityServerConfiguration.AllowedScopes, + AccessTokenType = identityServerConfiguration.AccessTokenType, + Enabled = identityServerConfiguration.Enabled, + RequireClientSecret = identityServerConfiguration.RequireClientSecret + } + }).AddResourceOwnerValidator(); + + + var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder)); + var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance); + var baseSchemeUrlAndPort = urlFinder.Find(); + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + + services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) + .AddIdentityServerAuthentication(o => + { + o.Authority = baseSchemeUrlAndPort + "admin"; + o.ApiName = identityServerConfiguration.ApiName; + o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps; + o.AllowedScopes = identityServerConfiguration.AllowedScopes; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = identityServerConfiguration.ApiSecret; + }); + if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword)) + { + identityServerBuilder.AddDeveloperSigningCredential(); + } + else + { + var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword); + identityServerBuilder.AddSigningCredential(cert); + } + } return services; } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 6ac1d5a2..108b452b 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Reflection; using IdentityServer4.AccessTokenValidation; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Ocelot.Authentication.Middleware; using Ocelot.Cache.Middleware; using Ocelot.Claims.Middleware; +using Ocelot.Controllers; using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Errors.Middleware; @@ -179,28 +181,12 @@ namespace Ocelot.Middleware if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null) { - var urlFinder = (IBaseUrlFinder)builder.ApplicationServices.GetService(typeof(IBaseUrlFinder)); - - var baseSchemeUrlAndPort = urlFinder.Find(); builder.Map(configuration.AdministrationPath, app => { - var identityServerUrl = $"{baseSchemeUrlAndPort}/{configuration.AdministrationPath.Remove(0,1)}"; - - services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) - .AddIdentityServerAuthentication(o => - { - o.Authority = identityServerUrl; - o.ApiName = identityServerConfiguration.ApiName; - o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps; - o.AllowedScopes = identityServerConfiguration.AllowedScopes; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = identityServerConfiguration.ApiSecret; - }); - + app.UseMvc(); app.UseIdentityServer(); app.UseAuthentication(); - app.UseMvc(); }); } } diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index ddac1e2c..b20c490e 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -26,6 +26,7 @@ + @@ -36,7 +37,6 @@ - diff --git a/test/Ocelot.ManualTest/tempkey.rsa b/test/Ocelot.ManualTest/tempkey.rsa new file mode 100644 index 00000000..f1c5d01d --- /dev/null +++ b/test/Ocelot.ManualTest/tempkey.rsa @@ -0,0 +1 @@ +{"KeyId":"6aad821b3e366a0189ea6c9741eae6ba","Parameters":{"D":"RcfIF/iJ8qnKpHlaJCa9Qz+iN9Z655mfW0B/CycZx0WDQwQtjYNF+ijEkqfpGC3TJ9n19vXHdDEGfONxHwTtgS6PP/VIYYmql7OfCn+tkZUvMeIXykfEXFoNoWJXlT3eMI1JWyZpT/dZJLtmdeY09csDU/LjXTlyFrljW361T0NR/azAHqEfeuoKhqaJ2klzTzjif4xO5kMcTBHVyxrZm0+cbowsKPjI1QRh5xXsst8EDrM7rXStz4enneNaNbvP1nmWx++F7zn+5/WBDcPJVnL1HiyAzMAHj+oXG2JwDizO4RJxLbvQa2Y2jzoDp/qc++s2HWFo1PmuUnOzNIQjyQ==","DP":"x3VwsILF1yo8puLB6TOcb4hMWngz8rqjBl3dty4E3Un7UexVh+NkqTiSXWZNern6Ka6gE8CpdDXhQiYCFbcnBiSF6FVSpjpQ+Qf9PeRj5HipJF+DzGyEOTzwOiBjb6s5CvPUQWvJiqQoP1D+1V+X/+C0zug8+Df73UyHA7uieKc=","DQ":"aaZu9GfgKqUiy0uwPkmnLcwIEDqRlLG14c23OOoEqNRHK9T3OwUKEJ1qK0mbMKIVTwklyiC7uZHqMijIqk0LovTL4nI3LRlDkRjhUsIuubZZOAw7sYYemBUl+wEB/VZofaJ7H/CYtCUXyJhND2DFbTjzgeg3uWoMpyMDHuH/9Pc=","Exponent":"AQAB","InverseQ":"YRV4QA6rwB9BEHjzh5Wk3TcSS1CrwJWVopj/qC1amXxhtM3aMb4ZfKk7XoynLqHyQ86rB2p7dPNP2GL+fIWbt4h/ESO9JqOlM/bVXpdyCvIwchAL82hfHb4FRv+V+J2cksaIA+bHt7ye9n/XSmSr8v0WsjxN2qHzdla3t+J0c1I=","Modulus":"xLJZzQyYbJAqymvvJeig9H4cfXEy3t0KVnRuUumdSBzU/3F7q1vCDBkXLqs8icEv9ZL4MUgDzzSjjYJpVXzvC24L1My3NLhSSZOCGrhSHCx98+cAgrb71tirXXsiBMXKeGhnJ05KopHtPRJVxBvd3d3Kee957y86g1Sbho1XxwWsrzVu5E7YZS+NkJycHkiUseMKeQ+tMLbPoFZPu5EqrrsSWuDjb7XNUjJViyGaOtvL2SQ9QtvDu006fe4m0VVw71ycSt2ReAmlA+EgCFsyLYBIoAhlk7k+lKyYO3a/8E0bzltB6MGaRaGJMB0C9pSxAfGSmSpIi2OUy0YIpIoDoQ==","P":"ydx6DVoXf3DgS6WZtrR82xNf12kLD5cGUToPwwIjjX5oQywOhGXOV4GCrqISDff2bosrPvleBfuJ5KH9KRVAaEjh1At554Bq+Nw8cc/1mTXEOSKENDtA9GjkpthR0QW1FDFRR5Tc8sRuoBpulN1rJIDIkfEkqwlpugFmk2UrDk8=","Q":"+XNIV8qMorQ11C1fVj4L91wufF4NqVqCdm/PN3f+xZ5UWoiCOil+njRuIL09ZifEwy3fgqD06Fu/SvaqMODyKAzA+RMUJU0sk92aOzAhKiGBk38sEvEuDUKZYNJm5NLjo9XXBG8DQzSUPvmIFLaMCloA95Ozie0mJcrXcimCww8="}} \ No newline at end of file