AngularJS pass requestVerificationToken to a service
Asked Answered
M

3

6

I'd like to pass a RequestVerificationToken which is generated by a Razor MVC helper in my login form to an AngularJS service that I've done to manage the authentication of my application

my form is the following:

<div ng-model="loginRequest" >
        <form ng-submit="submit()"  ng-controller="loginCtrl">
            @Html.AntiForgeryToken()

            <input id="username" ng-model="loginRequest.Username"  type="text" name="text"  />
            <input id="password" ng-model="loginRequest.Password" type="text" name="text" />
            <input type="submit" id="submit" value="Submit" />
            <br/>
            isValid: {{loginRequest.isValid}}
            <br/>
            username: {{loginRequest.Username}}
            <br/>
            Password: {{loginRequest.Password}}
        </form>
    </div>

the @Html.AntiForgeryToken() renders in this way:

<input name="__RequestVerificationToken" type="hidden" value="AVzyqDKHSPjaY7L_GTpkasMAABRQRVRFUkFMSUVOV0FSRVxQZWRybwA1">

my AngujarJs controller injects successfully my "loginService" and I can send via Post the username and the password to the service

function loginCtrl($scope, loginService) {
   $scope.submit = function () {
      loginService.authenticate($scope.loginRequest,function(data) {
          $scope.loginRequest.isValid = (data.User!=null);
          //console.log(data);
      });

   };
}

the service:

angular.module('App.services', ['ngResource']).
    factory('loginService',
        function ($resource) {
            return $resource('/Api/User/login', '',
                {
                        authenticate: {
                        method: 'POST',
                        isArray: false,
                        headers: { 'X-XSRF-Token': '?????' }
                    }
                });
        });

my question is how can I read the token rendered in the form and pass it to the service and set a header with the token taken from the login form, as far I as Know is not a good practice to manipulate the DOM and I don't know if I'll need to create a directive to perform that task so any suggestions are welcome!

Mertz answered 7/8, 2013 at 0:7 Comment(3)
Hope you have looked at infoq.com/news/2012/10/anti-forgery-aspnet-json and #15574986Manns
yes I did, I´m using servicestack for it I just got everything covered I just need to know how to pass the parameter to the service! in fact I think I'm following a very similar approach like the one described here #15445281Mertz
did my answer help you at all?Vanlandingham
L
14

I think I've found a pretty good solution. I started with the suggestions here http://www.novanet.no/no/blog/olav-nybo/dates/2013/12/anti-forgery-tokens-using-mvc-web-api-and-angularjs/ but the problem was that the directive is executed after the controller. So if you wanted to retrieve initial data in the controller it was very difficult.

Instead of using the directive just use the Module.run() method as referenced in the $http service documentation under the Setting HTTP Headers section http://docs.angularjs.org/api/ng.$http.

I used the HtmlHelper extension referenced in the blog post above with the body element and then my Module.run() looks like this:

myModule.run(['$http', function($http) {
    $http.defaults.headers.common['RequestVerificationToken'] = angular.element("body").attr('ncg-request-verification-token');
}]);

I think it makes for a pretty elegant solution.

Lowercase answered 13/1, 2014 at 15:8 Comment(0)
V
3

sadly, the easiest way is to include jquery.js into your project before angular.js, then do this:

headers: { 'X-XSRF-Token': angular.element('input[name="__RequestVerificationToken"]') }
Vanlandingham answered 7/8, 2013 at 16:33 Comment(10)
hey @Jason More, I just would like to know a way without using JQuery it supposed that not recommended, I'm learning angularJS right now so I'd like to start with the right foot since the beginning!Mertz
Unfortunately you are mixing two things - Server side rendering (razor) and client side templating (angular). jQuery is the glue between them. I'll ask around but after thinking on it more, I believe this is the correct wayVanlandingham
@Pedro You will have to do something to get it out of the DOM... you could add the bit of code Jason posted in to an Angular service/provider/factory and then inject that in to your login service, but really that is just adding a layer of abstraction which may make it easier to test.Alecto
@Polaris878 yeah I'll keep that in mind thanks guys! I just need to try and Idea I'll keep you posted!Mertz
Guys, what could be the case? if I modify the way that @Html.AntiForgeryToken() renders and add an overload that accepts the name for the ng-model, I think that changes the whole picture right?Mertz
@Pedro if you can change the output, thats even better. Ideally you would make a directive that matches the output of whatever the AnitForgeryToken spits out, that passes the token into the service, which is then later consumed by $resource. But being able to use ng-model will at least let you send it to a controller.Vanlandingham
@Pedro you probably would want to use 'ng-init' for this task: code.angularjs.org/1.1.5/docs/api/ng.directive:ngInitVanlandingham
@JasonMore I try to get it using angular.element and I get a weird message on when I click the submit button "Accessing selectionDirection on an input element that cannot have a selection."Mertz
@Pedro googling appears to point out it is the AngularJS pluggin for chrome dev tools: chrome.google.com/webstore/detail/angularjs-batarang/… Try disabling it and see if the message goes away.Vanlandingham
@JasonMore I googled it too, the batarang is disabled I ended up to move the $resorce to the controller since the services are the first to load whatever it happens the value always will be null and because they are singletons it only loads one time so i figure it out that putting that method as a service isn't the best solution, at least for me, I'll update the post, but I really appreciate your help, the nd-init did some part of the trick! thanks! a lot!Mertz
D
0

I had this same problem and I solved it making a custom AntiForgeryToken method in an custom HtmlHelper.

public static IHtmlString AngularAntiForgeryToken(this HtmlHelper html, string ngModelName = "AntiForgeryToken")
{
    MvcHtmlString antiForgery = html.AntiForgeryToken();
    string antiForgeryString = antiForgery.ToString();
    Regex regex = new Regex(string.Format("value=[\"|']([a-zA-Z0-9+=/\\-_]+)[\"|']", ngModelName));
    Match match = regex.Match(antiForgeryString);
    string antiForgeryToken = string.Empty;
    if (match.Success)
        antiForgeryToken = match.Groups[1].ToString();

    string result = antiForgeryString.Replace("<input", string.Format("<input ng-model=\"{0}\" ng-init=\"{0} = '{1}'\"", ngModelName, antiForgeryToken));

    return new HtmlString(result);
}
Diversity answered 19/5, 2016 at 15:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.