Web API 2 - block all external calls

Is it possible to block all calls on my web api that do not come from the website itself?

I mean if my MVC app is running at http://www.domain.com and the web api at http://www.domain.com/api/service , I want the web api to only accept calls from the current application. External calls allowed.

I'm guessing maybe a message handler would be the best in this case?

+3


source to share


3 answers


Create a controller for the error page and catch all garbage requests, for example:



 config.Routes.MapHttpRoute("block", "{*something}", new { controller = "Error", action = "Get" });

      

0


source


You must implement token authorization using a delegation handler.

 public class AuthorizationHeaderHandler : DelegatingHandler
{
    public AuthorizationHeaderHandler(HttpConfiguration httpConfiguration)
    {

        //set the inner handler
        InnerHandler = new HttpControllerDispatcher(httpConfiguration); 
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IEnumerable<string> apiKeyHeaderValues = null;

        if (request.Headers.TryGetValues("X-ApiKey", out apiKeyHeaderValues))
        {
            var apiKeyHeaderValue = apiKeyHeaderValues.First();

            //based on the api-key get username whose session is stil valid.

            var username = //code to get user based on apiKeyHeaderValue;

            if (!string.IsNullOrEmpty(username))
            {
                var usernameClaim = new Claim(ClaimTypes.Name, username);
                var identity = new ClaimsIdentity(new[] {usernameClaim}, "ApiKey");
                var principal = new ClaimsPrincipal(identity);

                Thread.CurrentPrincipal = principal;
            }

        }
        else
        {
            //You don't have an ApiKey from the request... can't proceed
            var response = request.CreateResponse(HttpStatusCode.Forbidden,
                new {Message = "You are not Authorized to access that resource"}); //new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }

        return base.SendAsync(request, cancellationToken);
    }
}

      

Then you can register the handler in the WebApiConfig

 public class WebApiConfig
{
    public static void Init(HttpConfiguration config)
    {

        config.Routes.MapHttpRoute(
           name: "DefaultApi",
           routeTemplate: "api/{controller}/{action}/{id}",
           defaults: new { id = RouteParameter.Optional },
           constraints:null,
           handler: new AuthorizationHeaderHandler(GlobalConfiguration.Configuration)
       );


    }
}

      

you can set up your login controller to authorize the user and assign a token

public class UserController : ApiController
{



    public async Task<HttpResponseMessage> Login([FromBody] UserDTO userDTO)
    {
        // first perform user authentication.

        // clear all existing tokens for this  authorized user


        //create security token and save token of current user
        //You can store this in a database and use a repository to create these.
        // Tokens can be guids.  
        // await token creation

        return Request.CreateResponse(HttpStatusCode.OK, new {LogingResult = result, token = token});
    }
}

      



Once that user has a token, it can be used for Api requests by adding to the request header. In Angularjs, this might look like this.

'use strict';

      

(function () {

angular.module('App', ['ngRoute', 'ngCookies']);

//interceptor for server calls

var httpInterceptor = function ($q, $window, $location) {
    return function(promise) {
        var success = function(response) {
            return response;
        };

        var error = function(response) {
            if (response.status === 403) {
                $location.url('/login');
            }

            return $q.reject(response);
        };

        return promise.then(success, error);
    };

}

httpInterceptor['$inject'] = ['$q', '$window', '$location'];
angular.module('App').factory('httpInterceptor', httpInterceptor);



var api = function ($http, $cookies) {
    return {
        init: function (token) {
            $http.defaults.headers.common['X-ApiKey'] = token || $cookies.token;
        }
    };
}

api['$inject'] = ['$http', '$cookies'];

angular.module('App').factory('api',  api);

      

}) ();

0


source


Yes, it is definitely possible. You must create your own handler and filter for the RemoteIpAddress found in the request. Here's an implementation using Owin standalone host:

 public class CustomerHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (request?.GetClientIpAddress() != "127.0.0.1")
        {
            return await Task.FromResult(request.CreateResponse(HttpStatusCode.Unauthorized));
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

public static class HttpReqestMessageExtension
{
    public static string GetClientIpAddress(this HttpRequestMessage request)
    {
        if (!request.Properties.ContainsKey("MS_OwinContext")) return null;

        dynamic owinContext = request.Properties["MS_OwinContext"];
        return owinContext.Request.RemoteIpAddress;
    }

}

      

If you are using ASP.Net you must use the appropriate key => MS_HttpContext

Now you just add this to your Api launch:

var config = new HttpConfiguration();
config.MessageHandlers.Add(new CustomerHandler());

      

0


source







All Articles