I program ASP.NET Framework MVC and Web API 2
I have to access a REST service for some information. The nature of the security requirements for this service require that I ask from a limited set of known IP addresses. The nature of my client requirements is that there will be an unknown number of them with IPs that are assigned by some DHCP. I think I need to stand up a proxy that will forward requests to the service and return responses to the client that asked. This server can be assigned a single static IP, that I can register with the target service. I don't want to try to duplicate the signatures of the target service and have to maintain my proxy whenever they decide to improve interfaces.
I would have the service that is restricting IPs and accepts a GET for http://S/action as an example. I would have the proxy at http://P/action. The client would send GET http://P/action and P would, in response, send GET http://S/action, collect the response, return it back to the client.
An attempt to implement this strategy, here is a handler I built for P that doesn't work:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
DelegatingHandler handler = new DelegatingHandlerProxy<ProxyHandler>();
config.MessageHandlers.Add(handler);
}
}
DelegatingProxyHandler is a way to get my dependency injection container involved:
public sealed class DelegatingHandlerProxy<THandler> : DelegatingHandler
where THandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
IDependencyScope scope = request.GetDependencyScope();
Task<HttpResponseMessage> task;
if (scope.GetService(typeof(THandler)) is DelegatingHandler handler)
{
if (!ReferenceEquals(handler.InnerHandler, InnerHandler))
{
handler.InnerHandler = InnerHandler;
}
HttpMessageInvoker invoker = new HttpMessageInvoker(handler);
task = invoker.SendAsync(request, cancellationToken);
}
else
{
throw new InvalidOperationException("Handler not registered with DI container");
}
return task;
}
}
The ProxyHandler that I want to do the work is:
public class ProxyHandler: DelegatingHandler
{
public ProxyHandler(
ITransformRequest preProcessor,
ITransformResponse postProcessor,
IForwardRequest forwarder)
{
PreProcessor = preProcessor;
PostProcessor = postProcessor;
Forwarder = forwarder;
}
private ITransformRequest PreProcessor { get; }
private ITransformResponse PostProcessor { get; }
private IForwardRequest Forwarder { get; }
#region Overrides of DelegatingHandler
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (PreProcessor != null)
{
request.RequestUri = PreProcessor.Transform(request.RequestUri);
}
HttpResponseMessage response = await Forwarder.Forward(request, cancellationToken);
HttpResponseMessage transformedResponse = PostProcessor.Transform(response);
return transformedResponse;
}
#endregion
}
In this case, the DI container supplies a PreProcessor that changes host, port, and prefix of the request to the target service. The Forwarder sends the request to the target using HttpClient. The PostProcessor will be a noop.
I didn't build any controllers. My thinking is that if this pipeline behaves as I expect, there won't be any controller that needs invoking. When I send ant request to this, http://P/anything returns a 404, not htto://S/anything. What am I missing?
Any particular reason you're not just writing a set of matching controllers that accept client requests and then execute the equivalent request on the 3rd arty API using a service that implements a simple web client and then returning the responses - perhaps including some authentication & caching logic to lower the impact on their API?
If your 3rd party API provider is limiting requests by IP, that is likely because they trust (or explicitly require) you to manage requests to their API in order to protect it from excessive load and/or security risks. Directly forwarding all client requests without any logic in your middleware means you're negating this limitation.
If the only purpose of your application is to provide a static IP (and you do not need to add any logic in your code) then you should consider using one of the many off the shelf API gateway products - e.g. Kong, which is an open source and very well established with plenty of community support https://konghq.com/kong-community-edition/
Related
I’m creating a class library (.NET Standard 2) where I need to pass a HttpClient to the constructor (Dependency Injection).
The class library is calling a third-party API using OAuth2 (It requests their API – with ClientID and ClientSecret – and get a token back which I’m going to use for subsequent calls).
I have some problems figuring out how to do the OAuth2 “stuff” with dependency injection.
The project I’m using the class library in – sets up the dependency in the StartUp class like: Services.AddHttpClient()
Can I somehow attach the OAuth2 “stuff” to the HttpClient?
What you could be doing is using a DelegatingHandler:
interface IOAuth2Stuff
{
string Token { get; set; }
}
class OAuth2StuffRequestHandler : DelegatingHandler, IOAuth2Stuff
{
public string Token { get; set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Add OAuth2 stuff to request
var token = Token;
return await base.SendAsync(request, cancellationToken);
}
}
Configure your services:
services.AddSingleton<OAuth2StuffRequestHandler>();
services.AddSingleton<IOAuth2Stuff>(provider =>
provider.GetRequiredService<OAuth2StuffRequestHandler>());
services.AddHttpClient("ServiceHttpClient")
.AddHttpMessageHandler<OAuth2StuffRequestHandler>();
And in your service class:
class Service
{
public Service(IHttpClientFactory clientFactory, IOAuth2Stuff oauth2)
{
var client = clientFactory.CreateClient("ServiceHttpClient");
// Do oauth2
// Set oauth2 token to request handler.
oauth2.Token = "blah blah";
}
}
Note that the lifetime of the middleware isn't perfect in the example.
One way you can achieve this is with a DelegatingHandler that you can add to the HTTP Client
This answer - https://stackoverflow.com/a/56204930/1538039 - describes adding the message handler to the HttpClient instance retrieved from a HttpClientFactory.
Within the AuthenticationDelegatingHandler class, it makes a call to GetToken -> which is something you'd need to provide that takes care of token retrieval/caching/renewal, use one of the the prebuilt nuget packages that can do this for you. (i.e. https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/ or equivalent)
Well, I know this is a bit of a tricky case. I have an ASP.NET Core site backend (site) that communicates with another system (facade) via http.
But not with HttpClient but with clients that facade provides with HttpClient underneath.
Constructors of these clients look like this
public class OrdersClient : IOrdersClient
{
private HttpClient _client;
public OrdersClient(IEndpoint endpoint, HttpClient client)
{ ... }
}
And registration looks like this
services
.AddHttpClient("facade-client")
.AddHttpMessageHandler<CorrelationIdDelegatingHandler>()
.AddHttpMessageHandler<HttpErrorDelegatingHandler>()
.AddTypedClient<IOrdersClient>((endpoint, client) => new OrdersClient(endpoint, client));
And configuration contains (it's important!)
app.UseCorrelationId(new CorrelationIdOptions());
N.B. What endpoint is for in this case is not important.
Ok. So I want to test my site with brand new cool WebApplicationFactory. I create it and get a client to the site as described in the docs. For some tests I just mock clients to facade like
public TestOrdersClient: IOrdersClient
{
}
and test my logic without facade.
Getting to the point. I want to test handlers that decorates my HttpClient to facade.
How I want to test handlers: I register fake PrimaryHttpMessageHandler so it will imitate answers from facade in my WebApplicationFactory. And I added some ImitationClient that used registered HttpClient.
services
.AddHttpClient("facade-client")
.ConfigurePrimaryHttpMessageHandler(() => new HttpMessageHandlerFake())
.AddTypedClient<ImitationClient>();
And here I am in trouble.
In my test a get ImitationClient from factory and try to call some method that calls HttpClient inside.
var factory = new SiteWebApplicationFactory();
var client = factory.CreateDefaultClient();
var imitationClient =factory.Server.Host.Services.GetService<ImitationClient>();
await imitationClient.GetData();
And here I get System.NullReferenceException because of this handler:
public class CorrelationIdDelegatingHandler : DelegatingHandler
{
private readonly ICorrelationContextAccessor _correlationAccessor;
public CorrelationIdDelegatingHandler(ICorrelationContextAccessor correlationAccessor)
{
_correlationAccessor = correlationAccessor;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add(
_correlationAccessor.CorrelationContext.Header,
_correlationAccessor.CorrelationContext.CorrelationId);
return base.SendAsync(request, cancellationToken);
}
}
Correlation Id assigned to incoming request to site and then forwarded to requests from site to facade. But when I test only client to facade I don't have incoming request, don't have CorrelationContext and fail.
Finally my question is how to correctly write test for inner httpClient with WebApplicationFactory. I know my description is rather confusing, I apologize for that. I am ready to answer any questions on the code and architecture of our project. Thank you!
I'm trying to come up with a good way to have a service layer that is mainly making REST API calls to an external API. I've currently got a .NET Core 2.0 project where my services are being injected into my controllers, and I'm making calls that way. However, in my services themselves, I'm making calls to external APIs that require an access token. My current architecture has mostly been thrown together pretty quickly just to kind of "get things working", but now I was to decouple things a bit more and make it more testable. Here is an example of one of my service methods to illustrate where I'm at, an implementation of ISomeSearchService:
public async Task<SearchDataResponse> SearchAsync(string query, string accessToken)
{
SearchDataResponsedataResponse = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("www.somesite.com/api");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
IList<KeyValuePair<string, object>> parameters = new List<KeyValuePair<string, object>>()
{
KeyValuePair.Create<string, object>( "searchTerm", query ),
};
var response = await client.GetAsync("/search" + UriFormatter.AsQueryString(parameters));
if(response.IsSuccessStatusCode)
{
string responseBody = await response.Content.ReadAsStringAsync();
dataResponse = JsonConvert.DeserializeObject<SearchDataResponse>(responseBody);
}
}
return dataResponse;
}
I know this is riddled with issues. Namely one of the biggest to me is that is constructing this HTTP client in the service itself. I would like to abstract that out, and maybe pass in a client to the service that is already constructed some way. That way in my tests, I can pass in mock clients to be able to test these methods without having to make actual HTTP calls. I'm not sure if there is a better way to handle that though. I can't find much guidance on service layers that are making HTTP calls. Most documentation I find is related to calling a DB directly.
Another issue is that I don't like passing the access token directly into the service. Since I was limited on time, I just did that to get things working, but I'm not happy with it.
Does anyone have some experience with this or a design that I could look into that would decouple this out more?
The goal is to inject an HttpClient instance. You can extract an interface from HttpClient to assist with mocking.
public interface IHttpClient : IDisposable
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
}
You will need an implementation that constructs instances of HttpClient but you can also use the interface to mock the request.
Now your service codes against the IHttpClient
public class SearchService : ISomeSearchService
{
private readonly IHttpClient httpClient;
public SearchService(IHttpClient httpClient)
{
this.httpClient = httpClient;
}
}
Alter your request so that you send an HttpRequestMessage instead of using .GetAsync() that way you can alter the Authorization header per request.
public async Task<SearchDataResponse> SearchAsync(string query, string accessToken)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri)
{
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
var response = await httpClient.SendAsync(request);
...
}
}
Then register the singleton HttpClient
services.AddSingleton<IHttpClient, HttpClientFactory>();
services.AddScoped<ISomeSearchService, SearchService>();
I've built a RESTful API (using ASP.NET Web API 2) which is only meant to be consumed from a single end-point. This end-point is a basic front-end site containing only HTML/CSS/JS. Due to various reasons, the front-end site and the API are completely external from one-another, with the front-end site being whitelisted in the API's CORS configuration.
I'm now trying to lock-down the API so that it's only accessible from this particular end-point, without introducing a new login system, because the context of where this page lives ensures that anyone accessing it is already a trusted user (it's technically behind a login system, but the page consuming the API has almost no knowledge of this context).
At a high level, I'd like to introduce a statically defined API Key of some sort, that would be hardcoded into both the API and the JavaScript of the consuming page, to help ensure that it's the only end-point accessing the API. We can assume that all communications between the front-end page and the API will be over a secure SSL/TLS connection.
My question: for such a case where I want to authenticate API requests from a particular page with a statically-defined API Key, what would be my best option from an ease-of-implementation standpoint? Most of the articles that I've found on Web API Authorization pivot around a user login system and seem grossly over-engineered for my particular use-case. I'd consider myself a novice when it comes to the subject and so I'm really just hoping for someone to point me in the right direction.
Thanks!
It seems like you are looking for a global filter in this specific case.
An authentication filter is a component that authenticates an HTTP request
You would basically send the shared / static api key with every request in the Authorization header and the custom filter would process this and decide whether the request is valid or not.
A basic implementation of the filter:
public class ApiKeyAuthenticationAttribute : IAuthenticationFilter
{
public bool AllowMultiple { get; set; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
// Get Auth header
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// Validate the static token
if (authorization?.Parameter == "123")
{
IPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim("CLAIMTYPE", "CLAIMVALUE") }));
context.Principal = principal;
}
else
{
context.ErrorResult = new AuthenticationFailureResult(request);
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
var challenge = new AuthenticationHeaderValue("Basic");
context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
return Task.FromResult(0);
}
}
And to enable it for all calls to your api add it to your WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Some more config here
config.Filters.Add(new IdentityBasicAuthenticationAttribute());
}
}
The AuthenticationFailureResult and AddChallengeOnUnauthorizedResult are implementations of IHttpActionResult. For comprehensiveness I will add them here.
AuthenticationFailureResult
class AuthenticationFailureResult : IHttpActionResult
{
private HttpRequestMessage _request;
public AuthenticationFailureResult(HttpRequestMessage request)
{
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = _request;
response.Content = new StringContent("ACCESS DENIED MESSAGE");
return Task.FromResult(response);
}
}
AddChallengeOnUnauthorizedResult
class AddChallengeOnUnauthorizedResult : IHttpActionResult
{
public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
{
Challenge = challenge;
InnerResult = innerResult;
}
public AuthenticationHeaderValue Challenge { get; private set; }
public IHttpActionResult InnerResult { get; private set; }
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await InnerResult.ExecuteAsync(cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Only add one challenge per authentication scheme.
if (!response.Headers.WwwAuthenticate.Any((h) => h.Scheme == Challenge.Scheme))
{
response.Headers.WwwAuthenticate.Add(Challenge);
}
}
return response;
}
}
This code is from or a derivative of this article Authentication Filters in ASP.NET Web API 2 and this article Authentication Filters in ASP.NET Web API 2
I am using HttpClient (aka Web API client) to consume RESTfull services.
Services require session to be established (via login) and then destroyed (via logout) upon each operation. So the call to consume service A looks something like this (pseudocode)
// setup
create auth dictionary authDict
create authenticationContent using FormUrlEndodeContent(authDict)
create cookieContainer
create HttpClientHandler...
create HttpClient
// login
await httpClient.PostAsync(LoginUrl, authenticationContent);
do error checking
// perform Operation A
await httpClient.....post...or...get...
extract data, process it, tranform it, get a cup of coffee, etc, etc
populate OperationAResult
// logout
await httpClient.GetAsync(LogoutUrl);
// return result
return OperationAResult
My question is, how can I easily reuse setup, login, and logout for different operations?
Should I be creating some method that will take in Action<> and if so how do I make sure that operations occur in order?
Probably the easiest way is to just write a wrapper class.
public class MyHttpClient
{
private HttpClient _client = new HttpClient();
private MyHttpClientSetup _setup;
public MyHttpClient(MyHttpClientSetup setup)
{
this._setup = setup;
}
private void HttpLogin()
{
// .. custom login stuff that uses this._setup
}
private void HttpLogout()
{
// .. custom logout stuff that uses this._setup
}
public void Reset()
{
this._client = new HttpClient();
}
// Wrapped Properties from the private HttpClient (1 example)
public Uri BaseAddress
{
get{ return this._client.BaseAddress;}
set{ this._client.BaseAddress = value;}
}
// Wrapped HttpMethods (1 example)
// Extremely poorly written, should be delegated properly
// This is just a bad example not using Task properly
public Task<HttpResponseMessage> DeleteAsync(string requestUri)
{
this.HttpLogin();
Task<HttpResponseMessage> result = this._client.DeleteAsync(requestUri);
this.HttpLogout();
return result;
}
public class MyHttpClientSetup
{
// Properties required for setup;
}
}
You may be able to create a new MessageHandler to handle this stuff for you transparently.
public class ConnectionHandler : DelegatingHandler {
public HttpClient HttpClient {get;set;}
public TestHandler(HttpMessageHandler handler) {
this.InnerHandler = handler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Do your login stuff here
return base.SendAsync(request, cancellationToken) // Make your actual request
.ContinueWith(t => {
// Do your logout stuff here
}
}
}
Then you can just use a single instance of a HttpClient to do all your requests. To add your handler to the request/response pipeline you just need to create a regular HttpClientHandler, assign it to the InnerHandler property of your DelegatingHandler and then pass your new handler into the constructor of the HttpClient. From that point on, all requests made via the HttpClient will be routed through your ConnnectionHandler.
var connectionHandler = new ConnectionHandler(new HttpClientHandler());
var client = new HttpClient(connectionHandler);
connectionHandler.HttpClient = client;
var response = client.GetAsync("http://example.org/request").Result;
The advantage of using a single HttpClient instance is that you don't have to keep re-specifying the DefaultRequestHeaders. Also, disposing the HttpClient will kill the TCP Connection so the next request will have to re-open it.