MiniProfiler and AngularJS - asp.net-mvc

Recently I was trying to profile ASP MVC methods called with AngularJS $http service and I noticed that MiniProfiler does not update the data as it does for AJAX calls from JQuery, for example.
Is there a way to use MiniProfiler with Angular ?

Adjustment needed for Angular (1.3.10)
(not needed if you are using another library for your XHR-needs)
MiniProfiler does this to XMLHttpRequest to be able to intercept all XHR-calls for angular
XMLHttpRequest.prototype.send = function sendReplacement(data) {
if (this.onreadystatechange) {
...
Well, Angular never sets xhr.onreadystatechange so we need to adjust this in a harmless way:
function createXhr() {
var xhr = new window.XMLHttpRequest();
xhr.onreadystatechange = function () { };
return xhr;
}
Explained in detail here

This issue was addressed with this pull request and has been fixed in the current MiniProfiler nuger.

Related

.NET Core MVC/Azure AD - 302 Found when making an ajax request

I am using Azure AD along with asp.net core mvc. The following code is the same with a default MVC project generated with Work or School Accounts authentication.
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
Everything works just fine for the most time. The app is basically a notepad. A user logs in and adds notes/tasks. Everything after logging in is done using ajax requests. After some time the app stops working because there is a need for authentication again. Note that if I refresh the page everything is working again.
Am I doing this right? Am I missing something or this kind of use case is not supported.
Should I just create a javascript function that will auto refresh the page after some time?
Should I just create a javascript function that will auto refresh the page after some time?
You could try to create a hidden iframe in all the templates used by the Web App to make automatic calls to a MVC controller method that forces a call to renew the authentication data on a regular basis.
This is achieved very easily by configuring an automatic javascript process in the front-end executed in a loop on a regular basis of 45'. This value could be configured or extracted from a configuration file too. The only key condition is that it must be less than one hour.
Here is the simplified example code related to MVC Controller:
/* Action method, inside "Account" controller class, to force renewal of user authentication session */
public void ForceSignIn()
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
And here is the simplified example HTML and javascript code employed to call silently in a hidden iframe to MVC Controller:
<iframe id="renewSession" hidden></iframe>
<script>
setInterval( function ()
{ #if (Request.IsAuthenticated) {
<text>
var renewUrl = "/Account/ForceSignIn";
var element = document.getElementById("renewSession");
element.src = renewUrl;
</text>
}
},
1000*60*45
);
</script>
For more details, you could refer to this article which get the similar situation with you.
I found a simple solution by accident. My goal was to hit a check endpoint every minute and If I get a 302 status code I would redirect the user to the authentication page.
public IActionResult Check()
{
return Ok(new { });
}
I left the developer tools open and noticed that every 30 mins I get a bigger response.
And this actually refreshes the cookie and as a result there is no need to redirect the user.
So to sum up someone needs to do this check every 40-50 minutes because the expiration is set to ~1 hour by default.

Preventing CSRF in Angular 2 / ASP.NET MVC application

I am working on a sample SPA using ASP.NET MVC for back end and Angular 2 for front end.
I followed below steps to prevent cross site request forgery attacks in my application
Since ASP.NET MVC sends a cookie with name "__RequestVerificationToken", and expects a header with name "__RequestVerificationToken" in the HTTP request to prevent CSRF , I have added below code in my angular module
{provide: XSRFStrategy, useFactory: xsrfFactory}
where xsrfFactory is below function
export function xsrfFactory() {
return new CookieXSRFStrategy('__RequestVerificationToken', '__RequestVerificationToken');
}
And below is the controller action code with "[ValidateAntiForgeryToken]" attribute , to which an AJAX call will be made using Http service of Angular 2.
[CustomAuth]
[ValidateAntiForgeryToken]
public ActionResult GetAuthors()
{
List<BookStoreAdmin.ViewModels.Author> authors = BookStoreAdmin.BAL.Author.GetAuthors();
BookStoreAdmin.ViewModels.Response<List<BookStoreAdmin.ViewModels.Author>> response = new Response<List<ViewModels.Author>>();
response.success = true;
response.errorMessage = null;
response.data = authors;
return Json(response, JsonRequestBehavior.AllowGet);
}
Below is the code which makes the AJAX call .
loadAuthors(): Observable<AuthorModel[]> {
return this.http.get('http://localhost:57599/author/GetAuthors')
.map((data) => data.json());
}
When my application makes an AJAX call using Http angular service , I was expecting it to have request header with name "__RequestVerificationToken" , but this
header is missing , any idea what could be the reason ?
Please let me know if more information needs to be provided ?
I can't see if you are passing a header in angular2 http call.
You can use RequestOptions API which allows you to add header. After adding header when you make request, ValidateAntiForgeryToken should be able to receive sent header.
Read more of RequestOptions here :
https://angular.io/docs/ts/latest/api/http/index/RequestOptions-class.html
Late answer but might be useful for someone.
I think the header is not set because this is a GET request. Though this is Angular 2, the angular 4 security docs might be relevant here as they state that
By default, an interceptor sends this cookie on all mutating requests (POST, etc.) to relative URLs but not on GET/HEAD requests or on requests with an absolute URL.
In order to explicitly include this header as #micronyks states in his answer you can use the RequestOptions API. Here's a code sample
var headers = new Headers();
headers.append('__RequestVerificationToken', <token>);
return this.http.get(url, {headers: headers});

TypeScript with Angular.JS and web API

I'm working on an asp.mvc3 web api project. in this project I use TypeScript and Angular.js
and I need to access the business layer from TypeScript through the Web API. I called the Web API inside the constructor method in TypeScript using the
code given below.
constructor($scope, $http: any) {
$scope.VM = this;
$http.get("/API/PrivateAPI/Test/1").success((a) => { this.Tiles = a });
var bb = this.Tiles;
}
However, when trying to get the object list from the business layer, the Tiles array is empty. I debugged the code and found out the Web API is called after passing the last line of the constructor and does return results. I need to call that method inside the constructor and get object list to the Tiles array.
Does anyone know how to do so?
First of, I think you should do the following (notice .data) :
$http.get("/API/PrivateAPI/Test/1").success((response) => { this.Tiles = response.data });
Anyways, $http only supports async http requests. What you want can be done by a synchronous XHR request and that is considered bad UI experience, since the browser window freezes till the XHR request completes, and therefore $http doesn't support it (configuration docs).
What you can do is something like :
call another function from response e.g.
(response) => { this.Tiles = response.data; this.continueWithProcessing(); }
Or, Setup a variable to hide a preloader when the response comes back:
(response) => { this.Tiles = response.data; this.allDone=true; }
Where you have an ng-show on something like:
<div ng-show="!VM.allDone">Loading the data....</div>
Or both :)
Note: An async setting is supported in underlying browsers native XHR object and therefore in $.ajax which is the jquery ajax function : http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings. However it is a horrible UI experience + if you use it from angular you are responsible for scope.apply.

SignalR across domains: errors with not allowed by Access-Control-Allow-Origin

Trying to call signalR from another domain, and keep on getting this error:
XMLHttpRequest cannot load
localhost:62150/signalr/negotiate?_=1362242757692. Origin
localhost:4982 is not allowed by Access-Control-Allow-Origin.
This is the code I'm trying to run:
$(function () {
jQuery.support.cors = true;
$.connection.hub.url = 'http://localhost:62150/signalr';
$.connection.hub.start()
.done(function () { alert("Now connected!"); })
.fail(function () { alert("Could not Connect!"); });
});
jquery and jquery.signalr.js are loaded, localhost:62150/signalr/hubs responds with JS, localhost:62150/signalr/hubs/negotiate?_=1362243021215 returns JSON if I run this in browser - so its not a missing script or invalid path issue.
What I've tried:
http://coding4life.wordpress.com/2012/03/30/making-cross-domain-calls-in-signalr/
(setting jQuery.support.cors and $.connection.hub.url)
Adding custom header in web.config with "Access-Control-Allow-Origin" value="*"
(this works only in IIS ? )
Creating an http module that would return this header on every request. Also tried to return actual domain name instead of *.
And combinations of all of the above.
Anyone has any idea what else I can try ?
The serving app is a combination of MVC and WebAPI (don't think it makes any difference).
If I'm trying that code from same domain - it works.
If you are using 1.0 or higher have you enabled cross domain on the server? (it's disabled by default now)
RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true });
After wasting a couple of hours I think it's good to share my experience:
DO NOT add Access-Control-Allow-Origin to your web.config (yes it never sais to add it, but when trying things this is literally the first I did and left it there after a simple jquery cross-domain access test)
RouteTable.Routes.MapHubs( new HubConfiguration() { EnableCrossDomain = true } ); works just fine for classes inheriting Hub
RouteTable.Routes.MapConnection<MyConnection>( "foo", "/foo", new ConnectionConfiguration { EnableCrossDomain = true } ); this works just fine too for classes inheriting PersistentConnection

Hash navigation problem while using jquery mobile with asp.net mvc2

I am looking to standardize the processing of ajax #anchors at the server side, using MVC.
Before a controller action is invoked I want to convert every request with ajax anchors into a request without ajax anchors, so that the controller code does not know there were anchors in the request:
For example:
1) /user/profile#user/photos should be treated as /user/photos
2) /main/index#user/profile/33 should be treated as /user/profile/33
What is the best technique in MVC to accomplish that?
This should necessarily be done on the client side, probably using jquery as everything that follows the # sign is never sent to the server.
I too struggle with same issue and I solved this problem after looking at Visual Studio 11 Developer Preview template code. I added following code in my _layout.cshtml, please note we must load jquery.mobile*.js file after following script tag:
<script type="text/javascript">
$(document).bind("mobileinit", function () {
// As of Beta 2, jQuery Mobile's Ajax navigation does not work in all cases (e.g.,
// when navigating from a mobile to a non-mobile page, or when clicking "back"
// after a form post), hence disabling it. http://jquerymobile.com/demos/1.0a3/#docs/api/globalconfig.html
#{
if (ViewBag.JqueryMobileAjaxEnabled != null && ViewBag.JqueryMobileAjaxEnabled == true)
{
#: $.mobile.ajaxEnabled = true;
}
else
{
#: $.mobile.ajaxEnabled = false;
}
}
});
</script>
**<script src="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.js"></script>**

Resources