Not so long ago, we discussed on this blog the possible ways of retrieving the client’s IP address in ASP.NET Web API.
With the latest changes in the Web API 2 infrastructure, and the emergence of Owin and the Katana project as the common glue between the underlying host and the web framework(s) running on it, it’s becoming natural to move these types of application-wide concerns (security, logging etc) to Owin middleware.
Let’s have a look at how you could – as an introductory example – obtain client’s IP address at the Owin middleware level, and why is it beneficial.
The benefits of Owin
We’ll not really go into the basic details of Owin here – I have already covered that in previous posts – so let’s just say that the gist is that with Owin we can easily host a number of frameworks side by side and decouple our web frameworks from the host beneath it. Naturally, through Owin middleware, we can address common concerns in a single place too – the most obvious usage being security.
If you are used to working with HttpMessageHandlers, the idea behind OWIN middleware is very similar – as they are chained one after another and allow you to modify the incoming request or outgoing response.
For quite a while, working with OWIN middleware meant dealing with quite a raw API, as you’d have to handle constructs such as Func<idictionary<string, object="">, Task>.
Now, Microsoft.Owin.dll provides a base abstract class for creating Owin middleware easily:
public abstract class OwinMiddleware { protected OwinMiddleware(OwinMiddleware next); protected OwinMiddleware Next { get; set; } public abstract Task Invoke(OwinRequest request, OwinResponse response); }
With that in place, it’s almost like implementing a MessageHandler.
Getting started with IP example
To get started we’ll need a new console project and following packages:
- – Katana: install-package Microsoft.Owin.Hosting -pre
- – Katana Http Listener: install-package Microsoft.Owin.Host.HttpListener -pre
- – Web API adapter: install-package Microsoft.AspNet.WebApi.Owin -pre
These 3 packages will bring in some additional dependencies they have.
We can now start a simple Owin server with Web API host – this is nothing new and should be very straight forward.
class Program { static void Main(string[] args) { string uri = "http://localhost:999/"; using (WebApp.Start<Startup>(uri)) { Console.WriteLine("Started"); Console.ReadKey(); Console.WriteLine("Stopped"); } } } public class Startup { public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); app.UseWebApi(config); } } public class TestController : ApiController { public string Get() { return "Hello world!"; } }
So really just a simple Web API Test controller and a setup to wire it all together.
Adding Owin middleware for IP inspection
Now suppose you’d like to restrict specific IP addresses.
If we do it at Owin level, it would affect all the frameworks running in your process (perhaps you might want to add SignalR or NancyFx) – so sounds like a perfect place to do it, doesn’t it?
Well, it’s extremely easy. We simply inherit from OwinMiddleware class and implement the Invoke method.
public class IpMiddleware : OwinMiddleware { private readonly HashSet<string> _deniedIps; public IpMiddleware(OwinMiddleware next, HashSet<string> deniedIps) : base(next) { _deniedIps = deniedIps; } public override async Task Invoke(OwinRequest request, OwinResponse response) { var ipAddress = (string)request.Environment["server.RemoteIpAddress"]; if (_deniedIps.Contains(ipAddress)) { response.StatusCode = 403; return; } await Next.Invoke(request, response); } }
In this case, we also pass in the list of restricted IPs. When we add our middleware to the pipeline, we are allowed to pass in params object[] so we can send whatever we want into our middleware constructor (more on that in a second).
We can retrieve the client’s IP address by asking for server.RemoteIpAddress key of the Environment object on the OwinRequest – it’s an IDictionary<string, object=""> and contains everything that could be interesting for us.
Based on that we can either deny the request (let’s say send a 403 Forbidden status code), otherwise we continue on to the next middleware.
To plug this in we need to add the following in the Configuration method of the Startup class – notice that this is the moment that we can send in any params to the Middleware too:
var deniedIps = new HashSet<string> {"192.168.0.100", "192.168.0.101"}; //whatever app.Use(typeof(IpMiddleware), deniedIps);
Also, we want to add it before the call to setup Web API!
Trying it out
Now if we access from a client that’s allowed to see the API, he gets the correct response as expected:
If the client is restricted, we reply with the 403 directly from the middleware: