Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Authentication and Antiforgery token with Durandal SPA template

Some areas of my SPA need to be open to all users, and some areas require authentication. In these areas, it's the data loaded via AJAX that I want to protect.

I have an authentication service (see below), which I add as a dependency in my durandal main.js. The service is called:

authentication 

In my main.js I call

authentication.handleUnauthorizedAjaxRequest(function () {         app.showMessage('You are not authorized, please login')         .then(function () {             router.navigateTo('#/user/login');         });     }); 

It warns the user they are not authorized, and navigates user to a login view/viewmodel where they can enter details and try logging in.

Some questions that come to mind when building this authentication viewModel:

  • Are there any obvious concerns with what I'm doing?
  • Is this how I'm 'meant' to do things in Durandal?
  • Am I re-inventing the wheel? I couldn't see anything like this within Durandal.

Most people seem to be creating seperate cshtml pages; one for login (if the user is not authenticated), and the usual index.cshtml Is there any good reasons for me to switch to that method?

My login action on my the server-side 'user controller' has the [ValidateAntiForgeryToken] attribute I need to send that as well.
I also have an 'antiforgery' service (see below) which I also add as a dependency in my main.js viewModel file then (also in my main.js).

antiforgery.addAntiForgeryTokenToAjaxRequests(); 

This intercepts all ajax requests (along with content), and adds the MVC AntiForgeryToken value to the data. Seems to work exactly as I want it to. Please let me know if there's any errors/mistakes.

Complete authentication service below.

// services/authentication.js define(function (require) {     var system = require('durandal/system'),     app = require('durandal/app'),     router = require('durandal/plugins/router');      return {         handleUnauthorizedAjaxRequests: function (callback) {             if (!callback) {                 return;             }             $(document).ajaxError(function (event, request, options) {                 if (request.status === 401) {                     callback();                 }             });         },          canLogin: function () {                      return true;         },         login: function (userInfo, navigateToUrl) {             if (!this.canLogin()) {                 return system.defer(function (dfd) {                     dfd.reject();                 }).promise();             }             var jqxhr = $.post("/user/login", userInfo)                 .done(function (data) {                     if (data.success == true) {                         if (!!navigateToUrl) {                             router.navigateTo(navigateToUrl);                         } else {                             return true;                         }                     } else {                         return data;                     }                 })                 .fail(function (data) {                     return data;                 });              return jqxhr;         }     }; });  // services/antiforgery.js define(function (require) {     var app = require('durandal/app');      return {         /*  this intercepts all ajax requests (with content)             and adds the MVC AntiForgeryToken value to the data             so that your controller actions with the [ValidateAntiForgeryToken] attribute won't fail              original idea came from http://stackoverflow.com/questions/4074199/jquery-ajax-calls-and-the-html-antiforgerytoken              to use this              1) ensure that the following is added to your Durandal Index.cshml             <form id="__AjaxAntiForgeryForm" action="#" method="post">                 @Html.AntiForgeryToken()             </form>              2) in  main.js ensure that this module is added as a dependency              3) in main.js add the following line             antiforgery.addAntiForgeryTokenToAjaxRequests();          */         addAntiForgeryTokenToAjaxRequests: function () {             var token = $('#__AjaxAntiForgeryForm     input[name=__RequestVerificationToken]').val();             if (!token) {                 app.showMessage('ERROR: Authentication Service could not find     __RequestVerificationToken');             }             var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(token);              $(document).ajaxSend(function (event, request, options) {                 if (options.hasContent) {                     options.data = options.data ? [options.data, tokenParam].join("&") :     tokenParam;                 }             });         }      }; }); 
like image 971
stooboo Avatar asked Apr 05 '13 08:04

stooboo


People also ask

How do I decrypt Antiforgery tokens?

The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.

What is Antiforgery token in ASP.NET MVC?

To help prevent CSRF attacks, ASP.NET MVC uses anti-forgery tokens, also called request verification tokens. The client requests an HTML page that contains a form. The server includes two tokens in the response. One token is sent as a cookie.

What is the Antiforgery token used for?

The purpose of using anti-forgery tokens is to prevent cross-site request forgery (CSRF) attacks. It does this by submitting two different values to the server on any given POST, both of which must exist in order for the server to allow the request.

How is Antiforgery token validated?

Require antiforgery validationThe ValidateAntiForgeryToken attribute requires a token for requests to the action methods it marks, including HTTP GET requests. If the ValidateAntiForgeryToken attribute is applied across the app's controllers, it can be overridden with the IgnoreAntiforgeryToken attribute.


1 Answers

I prefer to pass the antiforgery token in the header. This way its easy to parse out of the request on the server because its not intermingled with your form's data.

I then created a custom action filter to check for the antiforgery token.

I created a post already on how to do this.

like image 98
Evan Larsen Avatar answered Sep 20 '22 15:09

Evan Larsen