Showing posts with label mvc. Show all posts
Showing posts with label mvc. Show all posts

7/28/18

ASP.NET MVC Apps on Virtual Directory with IIS Express

On previous articles, we learned how to deploy multiple ASP.NET MVC Apps on the same Azure Web App by using virtual applications.  We also learned that for some cases when more than one application defines the same routes, this may lead to an ambiguous routing request if not configured properly.

Previous Articles





In this article, we learn how to configure our development environment with a virtual directory and have a second app run on the same process which should simulate the environment on Azure.

IIS Express Configuration

Visual Studio Solutions contain a .vs folder with solution configuration information. In that folder, we can find a config folder with an applicationhost.config file.  This is the file that enables us to configure IIS Express when running apps from Visual Studio.

When we open the file, we should look for the sites node (xml node). This is where the sites/apps definitions can be found for a solution. In the case of a solution with two ASP.NET projects, we can find a setting similar to this:


<site name="ozkary.azure.vdir.main" id="2">

    <application path="/" applicationPool="ozkary.azure.vldir.main AppPool">

        <virtualDirectory path="/" physicalPath="d:\repos\ozkary.vdir\main" />

    </application>

    <bindings>

        <binding protocol="http" bindingInformation="*:61823:localhost" />

    </bindings>
</site>

<site name="ozkary.azure.vdir.admin" id="3">

    <application path="/" applicationPool="ozkary.azure.vdir.admin AppPool">

        <virtualDirectory path="/" physicalPath="d:\repos\ozkary.vdir\admin" />

    </application>

    <bindings>

        <binding protocol="http" bindingInformation="*:62029:localhost" />

    </bindings>
</site>



In the settings, there are two sites (site node), main and admin.  Both of those sites run from a different local folder and a different port. If we translate this to an Azure deployment, we will need to deploy to two different web apps.

Our goal is to change this setting to only use one app, and deploy the admin site as a virtual app under the main site.  To do this using IIS Express, we need to configure the main app setting to read the following:


<site name="ozkary.azure.vdir.main" id="2">

<application path="/" applicationPool="Clr4IntegratedAppPool">
           <virtualDirectory path="/"    physicalPath="d:\repos\ozkary.vdir\main" />

</application>

<application path="/admin" applicationPool="Clr4IntegratedAppPool">
          <virtualDirectory path="/" physicalPath="d:\repos\ozkary.vdir\admin" />

</application>


<bindings>
        <binding protocol="http" bindingInformation="*:61823:localhost" />

</bindings>

</site>


To review, we just add another application setting under the same site node configuration. We need to be careful in setting the path information otherwise this can lead to errors. We set the new application node path attribute to the virtual directory name (/admin). We then set the virtualDirectory node attribute path to the root of the second project which should have a different physical path.  This essentially is the same as if we would do this on an IIS Server. 

Validate the configuration:

To validate that our configuration is working properly, we can take a look at the process that IIS Express is creating for us. We first take a snapshot of the process prior to making the virtual directory entry. If we run the projects, we would see that both projects are running with a different process ids,  PID. This is shown on this image below which is taken from the IIS Express admin app which is available from the system tray.


We can then stop the applications and add the additional application node under the main site.  We are now ready to lunch the applications again and take another snapshot.  We should now see that both applications are running under the same process id PID 25860.




After validating this locally, you can deploy to Azure and validate that this is working with no conflicts. To learn how to deploy to Azure using a virtual directory, review the article below:


  


Hope this is helpful and thanks for reading.

Originally published by ozkary.com

7/14/18

ASP.NET MVC Routing Error Multiple Controller Types on Azure Virtual Directory

In a previous article, I wrote about hosting an ASP.NET MVC app on a virtual directory on Azure.  You can find the article in the link below.  The main goal of that article was to show how to hosts other sites within the same Azure Web app.



There are cases when the multiple sites have similar routes and controllers, and the application find this ambiguous and does not know how to handle a request showing this error:



[InvalidOperationException: Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.

The request for 'Home' has found the following matching controllers:
Admin.Controllers.HomeController
Main.Controllers.HomeController]
               
This error is somewhat odd because a deployment to a virtual directory should isolate the routes per application and there should not be this ambiguity.

Review Deployment and Configuration

When facing such error, we need to check the following possible problems:

1) When doing the deployment, we need to make sure the destination URL includes the virtual directory folder
For example:   www.mysite.com/admin    where admin is the virtual directory folder.

2) Make sure the virtual directory root folder is not the same as the main site
This is a common mistake. When doing the configuration for a virtual directory we must make sure to set the correct physical path as shown next:


The image shows how the main application and virtual path use different physical paths. If both are set to the same physical path, then the routes will be processed from both apps with the same route but different controller types which causes the ambiguity.

Before doing the deployment to Azure make sure to test this configuration on your development environment. For those using Visual Studio with IIS Express, you may find this article useful:
I hope this is helpful.

Originally published by ozkary.com

11/5/16

HTTP verb used is not allowed

When writing RESTful APIs, we often used the HTTP verbs to control our operations.


Http Verb
Description
GET
Reads information
POST
Create a record
PUT
Update a record
DELETE
Delete a record

Our MVC APIs usually are set with attributes to indicate the verb as well.


During the development process, we test our APIs with IIS Express, so there is no unexpected behavior. When deploying to our test and production environments, the web server settings may be set to only allow the most common verbs like GET, POST and OPTIONS.  If this is the case when we deploy our API, we may come across this error when testing out the PUT or DELETE operations:


405 - HTTP verb used to access this page is not allowed


A quick way to control that from our applications is to set the allow verb configuration in our web.config file. This overrides the IIS settings for our application.  We can do this by setting the following web server configuration.


<system.webServer>  

    <security>
      <requestFiltering>      
          <verbs allowUnlisted="false">
            <add verb="GET" allowed="true" />
            <add verb="OPTION" allowed="true" />
            <add verb="POST" allowed="true" />
            <add verb="DELETE" allowed="false" />
            <add verb="PUT" allowed="true" />          </verbs>              
      </requestFiltering>
     </security>   

    <modules>
      <remove name="WebDAVModule" />
    </modules>

    <handlers>
      <remove name="WebDAV" />
    </handlers>

  </system.webServer>



The settings above indicate that we want to allow GET,POST, OPTION and PUT, and DELETE should not be allowed.  We also need to remove a WebDAV module and handler that can raise this error as they intercept some of the less common verb like PUT and DELETE.

The advantage of doing the changes on the web.config over configuring IIS is that as the application is deployed on any other server/environment; no one has to remember to update that setting on IIS.

Hope that helps.


Originally published by ozkary.com

3/6/16

Web API Anti-Forgery Token AngularJS Client

When building APIs for public web apps, we cannot add the authorization filters to the APIs because the end users do not have to authenticate. We can however add some security measures to the API which would protect it by allowing only requests from the same origin with an anti-forgery token thus allowing us to handle CSRF (Cross Site Request Forgery) threats. Let's take a look at how we can do this.

Common Scenario

  We have a public web app that allows users to send a contact request from the browser. We do not want to leave our API open to the world. We just want same origin requests to have access to our API. As an example, our web app is hosting a contact form as shown below. The contact request is sent by calling an API with anonymous access.

Contact Page

 The Sample Project can be found at this location:  https://github.com/ozkary/aspnet-samples


Anti-Forgery Token

 An anti-forgery or request verification token is used to protect a resource from a cross-site request forgery (CSRF). The ASP.NET MVC Framework provides an HTML helper that creates the token for us.  This is what we can use to insert a token in our contact form.

HTML Markup Design Mode

We use the Html helper with Razor syntax in our HTML markup as shown below:


<form method="post" id="contact" novalidate name="contactForm">
            @Html.AntiForgeryToken()


HTML Markup Output

After the page is rendered on the browser, the result of the AntiForgeryToken call produces the following HTML mark-up:


<input name="__RequestVerificationToken" type="hidden"
value="AMJ-A-9FvyoZMVpsapx2Dxr4drOUX_616Evo-Xxaq2ooscMXKOZG-qdQ3gzI0y4cekVotAaTql69J5yD1DCGqK4zzYM6c36Gm8DEtuxQkbw1" />



The helper call creates a cookie (_RequestVerificationToken) and a hidden input field with the same name and value. The idea is that this token becomes a signature of the forms that are served by our servers.   When the form is post back, both the cookie and input field are sent to the server for validation. This is when the anti-forgery validation takes place on a typical MVC controller action. In our case, we are using an API Controller, so let's take a look at this approach.

AngularJS Module:

To test our API, we create an AngularJS client. Our AngularJS module has a controller and service. The controller handles the interaction with the contact view. The service manages the request to our contact API.

//angular module
var app = angular.module('appContact', ['ngRoute', 'ui.bootstrap']);

//angular service
app.factory('app.svc.contact', ['$http', svcContact]);

//contact controller
app.controller('app.ctrl.contact', ['$scope', '$uibModal', 'app.svc.contact', 'app.const.contact','$log', ctrlContact]);


Since we are using an AngularJS service to send our requests instead of submitting a form, our approach is a bit different. We still need to send the token. but when we send the request via our service, we need to add the token in the request header. This is what an action filter will be parsing and validating on the server.


(function (angular) {
    'use strict';
    var app = angular.module('appContact');
    app.factory('app.svc.contact', ['$http', svcContact]);
   
    function svcContact($http) {
        var baseRoute = '/api/contact/';
        return {
            add: add
        }

        function add(contact) {
            var token = contact.token;
            if (token && contact.sendToken) {
                $http.defaults.headers.post['X-XSRF-Token'] = contact.token;
            } else {
                delete $http.defaults.headers.post['X-XSRF-Token'];
            }

            return $http.post(baseRoute+"send", contact);
        }
    }
})(angular);


In our contact form, there is a checkbox that is bound to the model property sendToken. If the option to send the token on the form is not checked (defaults to true to send the token), we remove the token from the header. This allows us to test the case with no token.

Validate Token Action Filter

The action filter can be used in our API Controller to block access to the APIs when the token is not valid.  Let's take a look at what the code for our action filter looks like:


[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
    public sealed class ValidateHttpAntiForgeryTokenAttribute : ActionFilterAttribute
    {
        private const string HeaderTokenName = "X-XSRF-Token";
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            var headers = actionContext.Request.Headers;          
            IEnumerable<string> tokens;

            try
            {
                if (headers.TryGetValues(HeaderTokenName, out tokens))
                {
                    var headerToken = tokens.FirstOrDefault();
                    var cookie = headers.GetCookies().Select(c => c[AntiForgeryConfig.CookieName]).FirstOrDefault();
                    var cookieToken = cookie != null ? cookie.Value : null;
                    AntiForgery.Validate(cookieToken, headerToken);
                }
                else
                {

                    string msg = HttpStatusCode.ExpectationFailed.ToString()+ " - " + HeaderTokenName;
                    throw new InvalidOperationException(msg);
                }   
            } catch{
                actionContext.Response = new HttpResponseMessage
                {
                    RequestMessage = actionContext.ControllerContext.Request,
                    StatusCode = HttpStatusCode.Forbidden,
                    ReasonPhrase = "Invalid Token"
                };                            
            }
                                          
            base.OnActionExecuting(actionContext);
        }      
    }



The code above checks the header for the X-XSRF-Token value and the anti-forgery cookie.  It then calls the AntiForgery.Validate helper method to validate that both values are matching. If this is not the case, or there is no token found on the header and exception is thrown and a forbidden (403) error is returned essentially blocking the access to the API.

API Controller with Action Filter

Since our action filter has been defined with the attribute usage of class or method, we can add the attribute at the method or class level in our API controller. In our case, we are using it at the class level. By decorating our class with this attribute (ValidateHttpAntiForgeryToken), we are adding a declarative security validation to all the methods in the class. 


    [RoutePrefix("api/contact")]
    [helper.ValidateHttpAntiForgeryToken]
    public class ContactApiController : ApiController
    {
        // POST: api/Contact       
        [HttpPost]
        [ResponseType(typeof(Contact))]
        [Route("send")]
        public async Task<IHttpActionResult> send(Contact contact)
        {
            IHttpActionResult result = null;

            if (!ModelState.IsValid)
            {
                result = BadRequest(ModelState);
            }
            else
            {
                //simulate delay for proceesing information
                await Task.Delay(1500);
                contact.Created = DateTime.Now;
                //TODO process contact information
                result = Ok(contact);               
            }

            return result;
        }
}



If the token is not valid, the action filter does not allow access to the api/contact/send route. In the code above, the send method just simulates some asynchronous action (delay) and adds the created date value to the contact request. It then just returns the contact information back to the client.

Demo the Contact Module

When we run the demo application, we can test with or without the token. If we look at the browser dev tools, we can examine the response for each of the requests. This can show us how a request that uses the token returns a HTTP status of 200 (success). Compare it to the request without the token which returns a status of 403 (forbidden – or no token).



When we look at the successful request header, we can see that both the cookie and custom header entries are sent. (highlighted below)



Not a Jason Web Token

We should note that the anti-forgery token is not a Jason Web Token (JWT) which is used for OAuth authentication when there is a login requirement. This is just a validation token to ensure that a request comes from the same origin (domain) and from an authorized client app.

I hope this is able to show you a simple way to protect your public APIs.

The Sample Project can be found at this location:  https://github.com/ozkary/aspnet-samples

Originally Published by ozkary.com