title: AJAX POST to Web API Controller and CSRF
body: <p>I basically need to protect against Cross Site Request Forgery in my Web API Controller that's a part of an MVC application. I am open to any ideas.  At this point, I have an MVC View that displays an Esri map using ArcGIS for JavaScript API. The user creates a route on the map, and the route and various features it crosses can be saved via an AJAX POST. The View does not have a form. This is because all data that I POST to the server is in memory and not visible on screen (or in hidden fields). </p>
  
  <p>So, I have the following in my MVC View to get antiforgery tokens:</p>
  
  <pre><code>@functions{
  public string GetTokenHeaderValue()
  {
      string cookieToken, formToken;
      AntiForgery.GetTokens(null, out cookieToken, out formToken);
      return cookieToken + ":" + formToken;
  }
  }
  </code></pre>
  
  <p>I had this hidden input in the View, but realized this is bad since it has both the 'form token' and cookie token used with AntiForgery.Validation:</p>
  
  <pre><code>&lt;input type="hidden" id="antiforgeryToken" value="@GetTokenHeaderValue()" /&gt;
  </code></pre>
  
  <p>Then, in a separate JavaScript file (not in script tag in View), I am doing a Http POST to my Web API Controller. This is where I add the tokens to the request headers:</p>
  
  <pre><code>var headers = {};
  headers['RequestVerificationToken'] = $("#antiforgeryToken").val();
  // Ajax POST to Web API Controller
  $.ajax({
      async: true,
      url: '/api/RouteData',
      type: 'POST',
      headers: headers,
      data: requestData,
      dataType: 'json',
      error: function (xhr, statusText, errorThrown) {
          console.log('Error saving route data! ' + errorThrown);
      },
      success: function (result) {
      }
  });
  </code></pre>
  
  <p>NOTE: the data that is getting POSTed in the body is all in memory inside a custom Dojo widget (since the page displays an Esri map using ArcGIS for JavaScript). There is not a form on the page since the user doesn't enter data.)</p>
  
  <p>Tying it all together on the server side in a Web API Controller:</p>
  
  <pre><code>[System.Web.Http.HttpPost]
  [ResponseType(typeof(RouteData))]
  public async Task&lt;IHttpActionResult&gt; PostRouteData(RouteDataViewModel routeDataVM)
  {
      try
      {
          HttpRequestMessage httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
          ValidateRequestHeader(httpRequestMessage);
      }
      catch (Exception ex)
      {
          _logger.Log(LogLevel.Error, ex, ex.Message);
          throw;
      }
          // Now that we know user is who they say they are, perform update 
  }
  
  void ValidateRequestHeader(HttpRequestMessage request)
  {
      string cookieToken = "";
      string formToken = "";
  
      IEnumerable&lt;string&gt; tokenHeaders;
      if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
      {
          string[] tokens = tokenHeaders.First().Split(':');
          if (tokens.Length == 2)
          {
              cookieToken = tokens[0].Trim();
              formToken = tokens[1].Trim();
          }
      }
      AntiForgery.Validate(cookieToken, formToken);
  }
  </code></pre>
  
  <p>AntiForgery.Validate is what validates the tokens.</p>
  
  <p>I've seen this <a href="http://stackoverflow.com/questions/4074199/jquery-ajax-calls-and-the-html-antiforgerytoken/12116344#12116344">SO post</a>, which has given me some ideas, but didn't quite solve the issue for me. A lot of credit is due to this <a href="https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks" rel="nofollow noreferrer">post on ASP.net</a> as well.</p>
  
  <p>What makes this different for me (I think) is that my JavaScript is in a separate file and cannot call the server-side Razor function to get the antiforgery tokens, right? Any ideas on how to protect against CSRF without a form?</p>