How to get [authorize] to work how I want? - c#

I have this link
#Ajax.ActionLink("comment", "CreateDebateComment", new { id = Model.DebateID}, new AjaxOptions
{
UpdateTargetId = "comment-entry-box",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
});
Which triggers this controller
[Authorize]
public PartialViewResult CreateDebateComment(int id)
{
DebateIDForComment = id;
return PartialView("_CreateDebateCommentPartial");
}
If the user is not logged in they get redirected to the LogOn page but it loads in the comment-entry-box div instead of redirecting to the Login Page
I also tried this variation
public PartialViewResult CreateDebateComment(int id)
{
if (!User.Identity.IsAuthenticated)
{
RedirectToAction("LogOn", "Account");
}
DebateIDForComment = id;
return PartialView("_CreateDebateCommentPartial");
}
But it does not redirect and will still load the partialView
Does anyone know how I can get this to function as I want ? I need the Log On page to load as normal and not in the comment-entry-box.

You may take a look at the following blog post in which Phil Haack explains how you could suppress the forms authentication module from redirecting to the LogOn page if the the request was an AJAX request and return a 401 HTTP status code which could be intercepted by the client and redirect accordingly.
So either add the HttpModule he showed or install his NuGet package to your project and then all you have to do is to register a global ajax event that will be trigerred every time some of your AJAX requests on the page hits a 401 from the server:
<script type="text/javascript">
$.ajaxSetup({
statusCode: {
401: function () {
window.location.href = '#Url.Action("logon", "account")';
}
}
});
</script>

What if instead of using [Authorize] (which I realize IS the question)
You check authorization in code (eg !User.Identity.IsAuthenticated) and you wrap your response in json, a false on the client side redirects to the login [via javascript]
True is followed by the data you want to display
ie
{ "Success": "false" }
or
{ "Success": "true", "data": "blah blah blah" }

Related

Redirect to new page from Server for ASP.Net MVC Ajax Request

I'm trying to call a method from another controller using RedirectToAction(). But it doesn't work. Could you please explain, what I'm doing wrong?
[HttpPost]
public ActionResult AddToWishList(int id, bool check)
{
var currentUser = WebSecurity.CurrentUserId;
if (currentUser != -1)
{
// ...
}
else
{
return RedirectToAction("Login", "Account");
}
}
I call the method in HTML:
<script>
$(document).ready(function () {
/* call the method in case the user selects a checkbox */
$("#checkbox".concat(#Model.Id)).change(function () {
$.ajax({
url: '#Url.Action("AddToWishList", "Item")',
type: 'POST',
data: {
id: '#Model.Id',
check: this.checked
}
});
});
});
It works if I use:
success: function (result) {
window.location.href = "#Url.Content("~/Account/Login")";
}
But I don't need to navigate to the Login() after every click, only if the user is not authorized. Could you please explain how I can use redirect in the controller?
You can response JavaScript which basically returns JavaScriptResult.
[HttpPost]
public ActionResult AddToWishList(int id, bool check)
{
return JavaScript("window.location='/Account/Login'");
}
To add to #Win's answer - I had a use case where I had a legacy ASPX file in my ASP.NET MVC application. And, I had to navigate to that ASPX page from my MVC action returning ActionResult. So, I did something like this to navigate to that ASPX page from my action. I had to create an absolute URL for navigation.
var myLegacyPageUrl = Request.Url.Scheme
+ "://"
+ Request.Url.Authority
+ Url.RequestContext.HttpContext.Request.ApplicationPath
+ "/mylegacypage.aspx?";
return JavaScript(string.Format("window.location='{0}'", errorPageUrl));
Hope someone finds it useful in case of legacy web pages present in modern MVC website.

ASP MVC Redirect to Request.UrlReferrer

I'm trying to implement multilanguage on my web application.
Everything was working fine but after some testing i've stumbled upon some weird interaction.
Let me first explain what i tried to do...
When language is changed, I tried to return the user to the page he visited when he tried to change the language on the web application. This is the code that I used to accomplish that.
var returnPage = Request.UrlReferrer;
if (returnPage != null)
{
return Redirect(returnPage.ToString());
}
return RedirectToAction("Index", "Home");
As I said before everything seemed to work, but on a single controller/action, this redirect is acknowledged as Async request. Which then breaks the code. I could fix the problem with some "spaghetti" but I want to do this cleaner.
Now why is Redirect sometimes acknowledged as Async request? Is there any other way of returning user to the last visited page without using Redirect and UrlReferrer so I can avoid that interaction?
What about something like the following...
Controller:
// after language is changed
if (Request.IsAjaxRequest()) {
return Json(new {
Success: true,
ReturnPage = returnPage ?? new UrlHelper(Request.RequestContext).Action("Index", "Home");
});
}
else {
if (returnPage != null)
{
return Redirect(returnPage.ToString());
}
return RedirectToAction("Index", "Home");
}
View:
$.ajax({
url: 'Settings/ChangeLanguage',
type: 'POST',
data: {lan: 'en'},
success: function(result) {
if (result.Success) {
window.location = result.ReturnPage;
}
}
});
That way the code in the controller would work for AJAX and non-AJAX requests. But if you are only calling it with AJAX, you might want to use only the code that returns json.

ADFS login error when Session variables used in MVC

I am getting this error when I use Session variable and I do the following:
1) Log in via ADFS and access the application
2) Do operations
3) Close all browser sessions. (Happens both in IE and Chrome)
4) I try to log in via ADFS again and it redirects about 4 times and it throws the error.
IIS has to be restarted to get the application working since it affects the application globally. None of the users can access it once this error occurs.
I narrowed it down to Session variables, even if I use TempData, ViewBag, ViewData it gives the error. If I remove the Session variables it works, it doesn't give me the error.
Any ideas why Session Variables are causing the error?
[HttpPost]
[ValidateAntiForgeryToken]
//Gets called on button clicked
public ActionResult SaveDoc()
{
Session["myList"] = bpc.UploadDocument(dto);
return Json(new { success = true, redirectToUrl = Url.Action("Summary") });
}
public ActionResult Summary()
{
return View();
}
[HttpPost]
public ActionResult GetResults_List([DataSourceRequest]DataSourceRequest request, CloudDTO dto)
{
var getResults = (List<SampleList>)Session["myList"];
return Json(getResults.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
Client Side
function btnSubmitClick() {
displaySpinner(true);
$.ajax({
url: '#Url.Action("SaveDoc", "Home")',
datatype: 'json',
data: {
__RequestVerificationToken: $('[name=__RequestVerificationToken]').val(),
URL: value,
CloudProvider: $("#cloudProvider").data("kendoDropDownList").text(),
},
type: "POST"
}).success(function (data) {
if (data.success) {
//On success we go to the Summary page.
window.location.href = data.redirectToUrl;
}
else {
//Error Message to user.
$("#validationMessage").html(data.message);
}
})
.done(function () {
displaySpinner(false);
});
I was having the exact same issue and I also realised it was down to the use of Session Variables.
The reason it is happening is because the Session Variables interfere with the cookie set by the Owin response headers. The workaround to this issue is to create a custom cookie manager. This resolved the issue for me
This Microsoft article explains the issue and also provides code samples for creating a custom cookie manager: https://blogs.msdn.microsoft.com/dsnotes/2016/08/25/owin-cookies-signin-error-with-ad-fs/

session timeout on ajax call

I know this is duplicate but I could not get reliable solution(for asp.net web).
I just want to redirect to the login page if session expires.
I have tried following:
1. using jquery status code
$.ajax({
type: "POST",
url: "stream.asmx/SomeMethod",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
//success msg
},
error: function (request, status, error) {
if (status = 403) {
location.href = 'login.aspx';
}
}
});
Problem: this returns same status code(403) for other errors too, which I only expect for session timeout.
2. Sending json message whether session expired
code behind:
if (!object.Equals(HttpContext.Current.Session["User"], null))
{
Id = int.Parse(HttpContext.Current.Session["User"].ToString());
}
else
{
result = from row in dtscrab.AsEnumerable()
select new
{
redirectUrl = "login.aspx",
isRedirect = true
};
}
on $.ajax success:
success: function (msg) {
if (msg.d[0].isRedirect) {
window.location.href = msg.d[0].redirectUrl;
}
else {
//load containt
}
}
Problem: It's somehow desn't invoke ajax success line if session expires(it does return correct json). And even this is not a proper way if I have many number of ajax request in the page(should be handled globally).
However, I saw this post which is really good soltion but it's for mvc using AuthorizeAttribute: handling-session-timeout-in-ajax-calls
So, Is there I can use same concept used in mvc using AuthorizeAttribute in asp.net web api? If not, how I can troubleshoot those issue which I'm facing (any of above two mentioned)?
A 403 status code is going to cause jQuery to call the failure method. Keep the same code behind from your second try, but move the redirect handler to the failure method instead of the success method. In the success method, treat it as you normally would.
Problem:
I had same problem in my Razor MVC Application throwing exceptions while ajax calls made when session timed out.
The way I have managed to get this issue sorted is by monitoring each ajax requests by using a simple light weight Action Method (RAZOR MVC) returning a bool variable whether the Request is Authenticated or not. Please find the code below..
Layout/Master Page / Script file:
<script>
var AuthenticationUrl = '/Home/GetRequestAuthentication';
var RedirectUrl = '/Account/Logon';
function SetAuthenticationURL(url) {
AuthenticationUrl = url;
}
function RedirectToLoginPage() {
window.location = RedirectUrl;
}
$(document).ajaxStart(function () {
$.ajax({
url: AuthenticationUrl,
type: "GET",
success: function (result) {
if (result == false) {
alert("Your Session has expired.Please wait while redirecting you to login page.");
setTimeout('RedirectToLoginPage()', 1000);
}
},
error: function (data) { debugger; }
});
})
Then in Home Controller/Server side you need a method to verify the request and return the boolean variable..
public ActionResult GetAuthentication ( )
{
return Json(Request.IsAuthenticated, JsonRequestBehavior.AllowGet);
}
This will validate each ajax request and if the session got expired for any ajax request, it will alert the user with a message and redirect the user to the login page.
I would also suggest not to use standard Alert to Alert. User some Tool tip kind of formatted div Alerts. Standard JS Alerts might force the user to click OK before redirection.
Hope it helps.. :)
Thanks,
Riyaz
Finally, I ended up following.
public class IsAuthorizedAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var sessions = filterContext.HttpContext.Session;
if (sessions["User"] != null)
{
return;
}
else
{
filterContext.Result = new JsonResult
{
Data = new
{
status = "401"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
//xhr status code 401 to redirect
filterContext.HttpContext.Response.StatusCode = 401;
return;
}
}
var session = filterContext.HttpContext.Session;
if (session["User"] != null)
return;
//Redirect to login page.
var redirectTarget = new RouteValueDictionary { { "action", "LogOn" }, { "controller", "Account" } };
filterContext.Result = new RedirectToRouteResult(redirectTarget);
}
}
Handling client side
<script type="text/javascript">
$(document).ajaxComplete(
function (event, xhr, settings) {
if (xhr.status == 401) {
window.location.href = "/Account/LogOn";
}
});
</script>
you can set session time out expire warning some thing like ....
<script type="text/javascript">
//get a hold of the timers
var iddleTimeoutWarning = null;
var iddleTimeout = null;
//this function will automatically be called by ASP.NET AJAX when page is loaded and partial postbacks complete
function pageLoad() {
//clear out any old timers from previous postbacks
if (iddleTimeoutWarning != null)
clearTimeout(iddleTimeoutWarning);
if (iddleTimeout != null)
clearTimeout(iddleTimeout);
//read time from web.config
var millisecTimeOutWarning = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeoutWarning"]) * 60 * 1000 %>;
var millisecTimeOut = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeout"]) * 60 * 1000 %>;
//set a timeout to display warning if user has been inactive
iddleTimeoutWarning = setTimeout("DisplayIddleWarning()", millisecTimeOutWarning);
iddleTimeout = setTimeout("TimeoutPage()", millisecTimeOut);
}
function DisplayIddleWarning() {
alert("Your session is about to expire due to inactivity.");
}
function TimeoutPage() {
//refresh page for this sample, we could redirect to another page that has code to clear out session variables
location.reload();
}
4xx are HTTP error status codes and would cause jquery to execute the onFailure callback.
Also, beware of using 3xx for redirects when you want to process the payload. Internet Explorer, in my experience, just does a redirect (without looking at the payload) when a 3xx status code is returned.
I'd say, throw a 403 and handle the situation. To the client 403 implies the resource access is forbidden. There can be multiple reasons, which is OK I guess.
For those using a ScriptManager, you can easily check for ajax request and then redirect with the following code:
private void AjaxRedirect(string url)
{
Response.StatusCode = 200;
Response.RedirectLocation = url;
Response.Write("<html></html>");
Response.End();
}
Then check for request type and redirect accordingly (using routes here):
if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
{
var redirectUrl = RouteTable.Routes.GetVirtualPath(null, "Default", null).VirtualPath;
AjaxRedirect(redirectUrl);
}
else
{
Response.RedirectToRoute("Default");
}
The "Default" route is a route defined in the routes collection:
routes.MapPageRouteWithName("Default", "", "~/default.aspx");
If you prefer, instead of using ScriptManager for ajax request check, you can use:
if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") {
code here...
}

How to redirect on the server side to override Angular.JS routing on a post?

I have created an ASP.NET MVC5 application which uses Angular.JS to control one of the pages. I am able to successfully post back to the ASP.NET controller using an $http.post, however, in the ASP.NET controller, if I try to return View("NewPage") or return RedirectToAction("NewPage", "Home"), it keeps me on my Angular.JS page.
Is there a way to override the client side Angular.JS routing, so that I can send the user to another view using the ASP.NET MVC controller?
Here's my Angular.JS post:
$http.post("/calculator/post", vm.data)
.then(function (response) {
// Success
// reset the data on a successful submit
vm.data = {};
vm.dataObjects = {};
}, function (error) {
// Failure
vm.errorMessage = "Failed to find rates.";
})
.finally(function () {
vm.isSubmitBusy = false;
});
Here's my ASP.NET controller:
//POST: Calculator
[HttpPost]
public ActionResult Post(CalcFormViewModel data)
{
if (ModelState.IsValid)
{
return View("NewPage");
}
TempData["Error"] = "An error occurred.";
return RedirectToAction("Error");
}
Using breakpoints in my ASP.NET controller, I can confirm that the return View() and return RedirectToAction() are being reached, however after a successful post, the page does not change in the browser.
That's because you're not asking the browser to navigate to a new resource. You're making a request with XMLHttpRequest. The response received by requests made with XMLHttpRequest can't affect the navigation of the browser. It would be down to you to interpret the response in the callback (then clause), and to force a page change by fiddling with window.location.href in your javascript.

Categories

Resources