mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 10:58:15 +08:00
test passing with authentication being provided by the user and mapped to the re route in config
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
/*
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Ocelot.Configuration;
|
||||
@ -57,3 +58,4 @@ namespace Ocelot.Authentication.JsonConverters
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
@ -2,52 +2,13 @@
|
||||
|
||||
namespace Ocelot.Configuration
|
||||
{
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class AuthenticationOptions
|
||||
{
|
||||
public AuthenticationOptions(string provider, List<string> allowedScopes, IAuthenticationConfig config)
|
||||
public AuthenticationOptions(List<string> allowedScopes)
|
||||
{
|
||||
Provider = provider;
|
||||
AllowedScopes = allowedScopes;
|
||||
Config = config;
|
||||
}
|
||||
|
||||
public string Provider { get; private set; }
|
||||
|
||||
public List<string> AllowedScopes { get; private set; }
|
||||
|
||||
public IAuthenticationConfig Config { get; private set; }
|
||||
}
|
||||
|
||||
public class IdentityServerConfig : IAuthenticationConfig
|
||||
{
|
||||
public IdentityServerConfig(string providerRootUrl, string apiName, bool requireHttps, string apiSecret)
|
||||
{
|
||||
ProviderRootUrl = providerRootUrl;
|
||||
ApiName = apiName;
|
||||
RequireHttps = requireHttps;
|
||||
ApiSecret = apiSecret;
|
||||
}
|
||||
|
||||
public string ProviderRootUrl { get; private set; }
|
||||
public string ApiName { get; private set; }
|
||||
public string ApiSecret { get; private set; }
|
||||
public bool RequireHttps { get; private set; }
|
||||
}
|
||||
|
||||
public class JwtConfig : IAuthenticationConfig
|
||||
{
|
||||
public JwtConfig(string authority, string audience)
|
||||
{
|
||||
Audience = audience;
|
||||
Authority = authority;
|
||||
}
|
||||
|
||||
public string Audience { get; }
|
||||
|
||||
public string Authority { get; }
|
||||
}
|
||||
|
||||
public interface IAuthenticationConfig {}
|
||||
}
|
||||
|
@ -4,18 +4,7 @@ namespace Ocelot.Configuration.Builder
|
||||
{
|
||||
public class AuthenticationOptionsBuilder
|
||||
{
|
||||
|
||||
private string _provider;
|
||||
|
||||
private List<string> _allowedScopes;
|
||||
|
||||
private IAuthenticationConfig _identityServerConfig;
|
||||
|
||||
public AuthenticationOptionsBuilder WithProvider(string provider)
|
||||
{
|
||||
_provider = provider;
|
||||
return this;
|
||||
}
|
||||
private List<string> _allowedScopes = new List<string>();
|
||||
|
||||
public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes)
|
||||
{
|
||||
@ -23,76 +12,9 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthenticationOptionsBuilder WithConfig(IAuthenticationConfig config)
|
||||
{
|
||||
_identityServerConfig = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthenticationOptions Build()
|
||||
{
|
||||
return new AuthenticationOptions(_provider, _allowedScopes, _identityServerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public class IdentityServerConfigBuilder
|
||||
{
|
||||
private string _providerRootUrl;
|
||||
private string _apiName;
|
||||
private string _apiSecret;
|
||||
private bool _requireHttps;
|
||||
|
||||
public IdentityServerConfigBuilder WithProviderRootUrl(string providerRootUrl)
|
||||
{
|
||||
_providerRootUrl = providerRootUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityServerConfigBuilder WithApiName(string apiName)
|
||||
{
|
||||
_apiName = apiName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityServerConfigBuilder WithApiSecret(string apiSecret)
|
||||
{
|
||||
_apiSecret = apiSecret;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityServerConfigBuilder WithRequireHttps(bool requireHttps)
|
||||
{
|
||||
_requireHttps = requireHttps;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityServerConfig Build()
|
||||
{
|
||||
return new IdentityServerConfig(_providerRootUrl, _apiName, _requireHttps, _apiSecret);
|
||||
}
|
||||
}
|
||||
|
||||
public class JwtConfigBuilder
|
||||
{
|
||||
public string _authority;
|
||||
|
||||
public string _audience;
|
||||
|
||||
public JwtConfigBuilder WithAuthority(string authority)
|
||||
{
|
||||
_authority = authority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtConfigBuilder WithAudience(string audience)
|
||||
{
|
||||
_audience = audience;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtConfig Build()
|
||||
{
|
||||
return new JwtConfig(_authority, _audience);
|
||||
return new AuthenticationOptions(_allowedScopes);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Creator.Configuration;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator
|
||||
{
|
||||
private readonly IAuthenticationProviderConfigCreator _creator;
|
||||
|
||||
public AuthenticationOptionsCreator(IAuthenticationProviderConfigCreator creator)
|
||||
public AuthenticationOptions Create(FileReRoute reRoute)
|
||||
{
|
||||
_creator = creator;
|
||||
}
|
||||
|
||||
public AuthenticationOptions Create(FileReRoute reRoute, List<FileAuthenticationOptions> authOptions)
|
||||
{
|
||||
//todo - loop is crap..
|
||||
foreach(var authOption in authOptions)
|
||||
{
|
||||
if(reRoute.AuthenticationProviderKey == authOption.AuthenticationProviderKey)
|
||||
{
|
||||
var authenticationConfig = _creator.Create(authOption);
|
||||
|
||||
return new AuthenticationOptionsBuilder()
|
||||
.WithProvider(authOption.Provider)
|
||||
.WithAllowedScopes(authOption.AllowedScopes)
|
||||
.WithConfig(authenticationConfig)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
|
||||
//todo - should not return null?
|
||||
return null;
|
||||
return new AuthenticationOptions(reRoute.AuthenticationOptions.AllowedScopes);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using Ocelot.Creator.Configuration;
|
||||
/*using Ocelot.Creator.Configuration;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
@ -34,4 +34,4 @@ namespace Ocelot.Configuration.Creator
|
||||
.WithRequireHttps(authenticationOptions.IdentityServerConfig.RequireHttps).Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
@ -92,7 +92,7 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var response = _configurationValidator.IsValid(fileConfiguration);
|
||||
var response = await _configurationValidator.IsValid(fileConfiguration);
|
||||
|
||||
if (response.Data.IsError)
|
||||
{
|
||||
@ -110,14 +110,14 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
foreach (var reRoute in fileConfiguration.ReRoutes)
|
||||
{
|
||||
var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration, fileConfiguration.AuthenticationOptions);
|
||||
var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
|
||||
return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath);
|
||||
}
|
||||
|
||||
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration, List<FileAuthenticationOptions> authOptions)
|
||||
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
||||
|
||||
@ -129,7 +129,7 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileReRoute, globalConfiguration);
|
||||
|
||||
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute, authOptions);
|
||||
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
|
||||
|
||||
@ -168,7 +168,7 @@ namespace Ocelot.Configuration.Creator
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithAuthenticationProviderKey(fileReRoute.AuthenticationProviderKey)
|
||||
.WithAuthenticationProviderKey(fileReRoute.AuthenticationOptions.AuthenticationProviderKey)
|
||||
.Build();
|
||||
|
||||
await SetupLoadBalancer(reRoute);
|
||||
|
@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IAuthenticationOptionsCreator
|
||||
{
|
||||
AuthenticationOptions Create(FileReRoute reRoute, List<FileAuthenticationOptions> authOptions);
|
||||
AuthenticationOptions Create(FileReRoute reRoute);
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
private bool IsAuthenticated(FileReRoute fileReRoute)
|
||||
{
|
||||
return !string.IsNullOrEmpty(fileReRoute.AuthenticationProviderKey);
|
||||
return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey);
|
||||
}
|
||||
|
||||
private bool IsAuthorised(FileReRoute fileReRoute)
|
||||
|
@ -7,14 +7,9 @@ namespace Ocelot.Configuration.File
|
||||
public FileAuthenticationOptions()
|
||||
{
|
||||
AllowedScopes = new List<string>();
|
||||
IdentityServerConfig = new FileIdentityServerConfig();
|
||||
JwtConfig = new FileJwtConfig();
|
||||
}
|
||||
|
||||
public string AuthenticationProviderKey {get; set;}
|
||||
public string Provider { get; set; }
|
||||
public List<string> AllowedScopes { get; set; }
|
||||
public FileIdentityServerConfig IdentityServerConfig { get; set; }
|
||||
public FileJwtConfig JwtConfig { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,9 @@ namespace Ocelot.Configuration.File
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>();
|
||||
GlobalConfiguration = new FileGlobalConfiguration();
|
||||
AuthenticationOptions = new List<FileAuthenticationOptions>();
|
||||
}
|
||||
|
||||
public List<FileReRoute> ReRoutes { get; set; }
|
||||
public FileGlobalConfiguration GlobalConfiguration { get; set; }
|
||||
public List<FileAuthenticationOptions> AuthenticationOptions { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace Ocelot.Configuration.File
|
||||
FileCacheOptions = new FileCacheOptions();
|
||||
QoSOptions = new FileQoSOptions();
|
||||
RateLimitOptions = new FileRateLimitRule();
|
||||
AuthenticationOptions = new FileAuthenticationOptions();
|
||||
}
|
||||
|
||||
public string DownstreamPathTemplate { get; set; }
|
||||
@ -33,6 +34,6 @@ namespace Ocelot.Configuration.File
|
||||
public FileQoSOptions QoSOptions { get; set; }
|
||||
public string LoadBalancer {get;set;}
|
||||
public FileRateLimitRule RateLimitOptions { get; set; }
|
||||
public string AuthenticationProviderKey {get; set;}
|
||||
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
||||
}
|
||||
}
|
@ -3,8 +3,6 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Ocelot.Authentication.JsonConverters;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
|
||||
@ -49,9 +47,7 @@ namespace Ocelot.Configuration.Repository
|
||||
|
||||
var json = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
var settings = new JsonSerializerSettings();
|
||||
settings.Converters.Add(new AuthenticationConfigConverter());
|
||||
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
|
||||
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json);
|
||||
|
||||
return new OkResponse<IOcelotConfiguration>(consulConfig);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Authentication.Handler;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
@ -10,7 +10,14 @@ namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
public class FileConfigurationValidator : IConfigurationValidator
|
||||
{
|
||||
public Response<ConfigurationValidationResult> IsValid(FileConfiguration configuration)
|
||||
private readonly IAuthenticationSchemeProvider _provider;
|
||||
|
||||
public FileConfigurationValidator(IAuthenticationSchemeProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
|
||||
{
|
||||
var result = CheckForDuplicateReRoutes(configuration);
|
||||
|
||||
@ -19,7 +26,7 @@ namespace Ocelot.Configuration.Validator
|
||||
return new OkResponse<ConfigurationValidationResult>(result);
|
||||
}
|
||||
|
||||
result = CheckForUnsupportedAuthenticationProviders(configuration);
|
||||
result = await CheckForUnsupportedAuthenticationProviders(configuration);
|
||||
|
||||
if (result.IsError)
|
||||
{
|
||||
@ -42,38 +49,27 @@ namespace Ocelot.Configuration.Validator
|
||||
return new OkResponse<ConfigurationValidationResult>(result);
|
||||
}
|
||||
|
||||
private ConfigurationValidationResult CheckForUnsupportedAuthenticationProviders(FileConfiguration configuration)
|
||||
private async Task<ConfigurationValidationResult> CheckForUnsupportedAuthenticationProviders(FileConfiguration configuration)
|
||||
{
|
||||
var errors = new List<Error>();
|
||||
|
||||
//todo - these loops break seperation of concerns...unit tests should fail also..
|
||||
foreach(var authProvider in configuration.AuthenticationOptions)
|
||||
{
|
||||
if (IsSupportedAuthenticationProvider(authProvider.Provider))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var error = new UnsupportedAuthenticationProviderError($"{authProvider.Provider} is unsupported authentication provider");
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
foreach (var reRoute in configuration.ReRoutes)
|
||||
{
|
||||
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationProviderKey);
|
||||
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions.AuthenticationProviderKey);
|
||||
|
||||
if (!isAuthenticated)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//todo is this correct?
|
||||
if(configuration.AuthenticationOptions.Exists(x => x.AuthenticationProviderKey == reRoute.AuthenticationProviderKey))
|
||||
var data = await _provider.GetAllSchemesAsync();
|
||||
var schemes = data.ToList();
|
||||
if (schemes.Any(x => x.Name == reRoute.AuthenticationOptions.AuthenticationProviderKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationProviderKey} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
|
||||
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions.AuthenticationProviderKey} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
@ -82,13 +78,6 @@ namespace Ocelot.Configuration.Validator
|
||||
: new ConfigurationValidationResult(false);
|
||||
}
|
||||
|
||||
private bool IsSupportedAuthenticationProvider(string provider)
|
||||
{
|
||||
SupportedAuthenticationProviders supportedProvider;
|
||||
|
||||
return Enum.TryParse(provider, true, out supportedProvider);
|
||||
}
|
||||
|
||||
private ConfigurationValidationResult CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(FileConfiguration configuration)
|
||||
{
|
||||
var errors = new List<Error>();
|
||||
|
@ -1,10 +1,11 @@
|
||||
using Ocelot.Configuration.File;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
public interface IConfigurationValidator
|
||||
{
|
||||
Response<ConfigurationValidationResult> IsValid(FileConfiguration configuration);
|
||||
Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Ocelot.Configuration;
|
||||
/*using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Creator.Configuration
|
||||
@ -7,4 +7,4 @@ namespace Ocelot.Creator.Configuration
|
||||
{
|
||||
IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions);
|
||||
}
|
||||
}
|
||||
}*/
|
@ -42,15 +42,10 @@ using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Creator.Configuration;
|
||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
@ -77,7 +72,6 @@ namespace Ocelot.DependencyInjection
|
||||
|
||||
services.Configure<FileConfiguration>(configurationRoot);
|
||||
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||
services.TryAddSingleton<IAuthenticationProviderConfigCreator, AuthenticationProviderConfigCreator>();
|
||||
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||
services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
|
||||
services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
|
||||
@ -149,38 +143,6 @@ namespace Ocelot.DependencyInjection
|
||||
services.AddIdentityServer(identityServerConfiguration, configurationRoot);
|
||||
}
|
||||
|
||||
//todo - this means we need to break auth providers into there own section in the config
|
||||
//then join onto them from reroutes based on a key
|
||||
var data = File.ReadAllText("configuration.json");
|
||||
var config = JsonConvert.DeserializeObject<FileConfiguration>(data);
|
||||
|
||||
foreach(var authOptions in config.AuthenticationOptions)
|
||||
{
|
||||
if(authOptions.Provider.ToLower() == "identityserver")
|
||||
{
|
||||
Action<IdentityServerAuthenticationOptions> options = o =>
|
||||
{
|
||||
o.Authority = authOptions.IdentityServerConfig.ProviderRootUrl;
|
||||
o.ApiName = authOptions.IdentityServerConfig.ApiName;
|
||||
o.RequireHttpsMetadata = authOptions.IdentityServerConfig.RequireHttps;
|
||||
o.SupportedTokens = SupportedTokens.Both;
|
||||
o.ApiSecret = authOptions.IdentityServerConfig.ApiSecret;
|
||||
};
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddIdentityServerAuthentication(authOptions.AuthenticationProviderKey, options);
|
||||
}
|
||||
else if (authOptions.Provider.ToLower() == "jwt")
|
||||
{
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(x =>
|
||||
{
|
||||
x.Authority = authOptions.JwtConfig.Authority;
|
||||
x.Audience = authOptions.JwtConfig.Audience;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.0.2" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
@ -42,7 +42,7 @@
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.3" />
|
||||
<PackageReference Include="Polly" Version="5.3.1" />
|
||||
<PackageReference Include="IdentityServer4" Version="2.0.1" />
|
||||
<PackageReference Include="IdentityServer4" Version="2.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Reference in New Issue
Block a user