FormsAuthentication.RedirectToLoginPage() vs RedirectToAction("Login", "Account") - asp.net-mvc

I am using Forms Authentication in my website. I have seen in some example code that one can call .SignOut() and then use
FormsAuthentication.RedirectToLoginPage()
to send a user to the login page.
What advantage, if any, does this have over calling
RedirectToAction("Login", "Account");
in an MVC website? From MSDN it seems that the former will not call HttpResponse.End() which means that code that follows will execute... I'm not sure when I would need to use this feature.

FormsAuthentication.RedirectToLoginPage() does have some advantage where it will attempt to append ?ReturnUrl={url} to the login page URL which can be used later to return the user to the page they were requesting when the login redirect happened. Using this method uses Response.Redirect() which goes against the MVC mentality. You'll lose out on events like OnActionExecuting from firing in your controller/filters. source code of RedirectToLoginPage
RedirectToAction("Login", "Account"); doesn't have the ReturnUrl feature out of the box, but it does keep everything in the MVC ecosystem so the events fire. In the case if logging someone out and redirecting back to the login page, I'd probably keep it all in MVC and use RedirectToAction. Or potentially use Redirect(FormsAuthentication.Url); if you always want to use the login page URL from the web.config.

Do a SignOut(), but then return a HttpUnauthorizedResult so that the controller will just use it's regular login redirect.
If you are signing out in your OnActionExecuting controller event, do it this way...
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
if (//I need to log out...) {
System.Web.Security.FormsAuthentication.SignOut();
filterContext.Result=new System.Web.Mvc.HttpUnauthorizedResult();
return;
}
base.OnActionExecuting(filterContext);
}

Related

Programmatically redirect unauthorized users using Microsoft.Owin.Security.OpenIdConnect

When I first started developing my MVC app (which requires authorization, to selectively render a SPA, which in turn does it's own authorization via ADAL.js) I simply decorated the HomeController's Index method with [Authorize] like thus,
[Authorize]
public ActionResult Index(bool ok = false)
{
return View();
}
and everything works fine.
However, sometimes I want users to share URLs with each other. My SPA uses hash-based routing to render various states. As long as both users are logged in already, they can share links with each other and everything magically works ok.
But, if the user clicking a link is yet-to-login, we lose the state communicated in the URL.. everything after the hash.. e.g. if user A sends unauthenticated user B this URL: http://mycorp.com/#/orders/85/edit the HomeController's going to notice user B is unauthenticated, and the middleware is going to kick user B over to our Microsoft login page.. and when he redirects back, the critical #/orders/85/edit portion of the URL in is gone.
My solution was to remove the [Authorize] attribute from the Index action, and instead do something like this:
public ActionResult Index(bool ok=false)
{
if (!User.Identity.IsAuthenticated)
{
if (!ok)
return View("SetCookie_Then_RedirectWith_OK_InQueryString");
if (ok)
//how do I do this part?
return new RedirectResult(Microsoft.Owin.Security.OpenIdConnect.Url);
}
else
//now we're authenticated,
//let JS resume by looking for cookie set previously
return View("Index");
}
Notice the how do I do this part comment above. That's my question.
+++++++++ FWIW ++++++++++
My app features a Startup.Auth.cs file with the familiar middleware declared...
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
...blah...
}
);
I presume I can do something fancy here in Startup.Auth.cs w/Notification Handlers but, I was hoping to directly call the middleware into action from the Index method.

My ASP.NET MVC app takes me back to the login page when I access the home controller even though I'm logged in

When I log in as a user, and type the url http://localhost:50562/Home/Index into the address bar, the browser takes me to the login page, which is my home page, but the funny thing is that the message "welcome, user!" and Logout link are still there in the upper right corner. When I click on the other links in the nav bar, the session continues normally, meaning, I was never logged out.
Can someone tell me how to configure the routing engine to take me to another page when I access the Home controller while being logged in?
You can achieve this by modifying the POST method for Login in the Account Controller (assuming you are using MVC 4 and the Simple Membership it uses).
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
...
}
You can set the routing in a few ways.
return RedirectToAction("Profile", "Home"); ... where Profile is a Controller
return View(returnUrl); ... where you have set and assigned the variable in your method
or retain as above with RedirectToLocal(returnUrl);
Good luck.
What you're describing happens because the user cookie isn't read until after the redirect to the home page is initiated. The best way to initialize your membership provider when users may enter on different pages is to use a global filter.
You can add this functionality for the SimpleMembership provider in the RegisterGlobalFilters method in the FilterConfig class in the App_Start folder
filters.Add(new YourAppNameSpace.Filters.InitializeSimpleMembershipAttribute());
Using a filter keeps you from having to repeat yourself with decorators all over your controller classes. It's easy to overlook adding decorators and, when they aren't there, your site will have unexpected bugs.
Thank you for your answers, but it seems that the project had a SessionObject class all along, which was handled by the controller. I am not the original author of the project, so it took me a while to figure it out. I was tasked to give it some additional features and bugfixes.
I ran into the method:
#region getSessionObject()
protected SessionObject getSessionObject() {
SessionObject loReturnValue = null;
if (Session[SessionObject.SESSION_CONSTANT] != null)
loReturnValue = (SessionObject) Session[SessionObject.SESSION_CONSTANT];
return loReturnValue;
}
It was all I needed to call in the index action of my home controller to check whether the user is logged in or not.

asp.net mvc Custom HandleUnauthorizedRequest

I'm working with asp.net mvc3.
I have a custom AuthorizeAttribute.
I have to override method HandleUnauthorizedRequest(AuthorizationContext filterContext)
My source code is as follows.
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
switch (unAuthorizedStatus)
{
case UnauthorizedStatus.NonAuthenticated:
// I have a question here.
break;
default: base.HandleUnauthorizedRequest(filterContext); break;
}
}
// I have a question here.
If it fails to user authentication(case UnauthorizedStatus.NonAuthenticated), to return to the previous URL,
And I want to show the warning window alert in JavaScript(or jquery).
To answer please.
Are you sure that you need to override the HandleUnauthorizedRequest method?
I followed the approach listed here:http://blogs.msdn.com/b/rickandy/archive/2011/05/02/securing-your-asp-net-mvc-3-application.aspx. Its so good they wrapped it up into MVC4.
Essentially, you handle the OnAuthorization method only. The rest you leave to the base Attribute. That has the nice feature of automatically routing you to the logon page if you are unauthenticated.
If you really want to auto navigate the user back to the original page, you can override the method as you were originally thinking, but instead send the user to a new action method, and pass the return URL from the HttpContext (referring url I think). You can then show a message to the user in html, and then auto navigate the user back to their original page after a couple of seconds wait via a meta refresh.
Your solution wont work if the end user has JS disabled.

Logout in MVC using Session without JavaScript/JQuery

Hi I'm relatively new to MVC 3 and I'm working with logging out my application, I managed to do the logging in, staying logged in even if website is opened on another tab.
All of my views (except for the home, of course) has this:
Logout
and in my controller I have this:
public ViewResult Logout()
{
Session.Abandon();
return View("Index", "Home");
}
and the app is not logging out, instead it is returning the current view.
Please help me understand what to do, and I would like to take note that I am using ViewResult instead of ActionResult, I'm not also going to use JavaScript or JQuery, because I'm making this app to show how MVC works.
Shouldn't you be pointing to the logout action?
Logout
Assuming the path to you're logout action is \Home\Logout
UPDATE:
Another old fashioned way is..
Upon successful login..
Session["Login"] = true; //or any object that describes the user's identity
On every page you need to check
var login = Session["Login"];
if(Convert.ToBoolean(login)){ //or cast to your expected object
//do something
}
else{
//redirect to logout/login page
}
Upon logout,
Session["Login"] = null;
If you are using forms authentication you should also clear the authentication cookie and redirect after logging out:
public ActionResult Logout()
{
Session.Abandon();
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}

PartialView Render Forms timeout

I'm fetching a partial view via $.ajax() and in the situation where you set idle for 30 minutes and then try to fetch that partial view, the forms authentication has timed out and instead of getting my partial view returned to me, I'm getting the login page render into my .
Any suggestions on how to deal with a situation like this?
Thank you.
$(function () {
$("#addContact").click(function () {
$.get('/Contacts/Add', function (data) {
$("#content").html(data); <--gets login page as data
});
});
});
Does your Add Action have any non-Ajax consumers? If not, I'd suggest removing the [Authorize] attribute from the action, which would remove the timeout-redirect problem. (If you have your entire controller decorated with [Authorize], you'd need to remove the controller-level attribute and adorn all of your other actions. Annoying, I know).
For extra security, you could then do something like this to prevent non-Ajax calls from calling your Add action.
public ActionResult Add()
{
if (Request.IsAjaxRequest())
return View("Error");
return View();
}
If, on the other hand, your Add action needs to support Ajax and normal calls, one way you can address this issue is to create a new Attribute class that inherits from and overrides AuthorizeAttribute. Check out the source for guidance: http://aspnet.codeplex.com/SourceControl/changeset/view/23011#266447
You should be able to do the trick by overriding the AuthorizeCore method, like so
public class AjaxAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
if (httpContext.Request.IsAjaxRequest())
return true;
return base.AuthorizeCore(httpContext);
}
}
Now you can use [AjaxAuthorize] on your controller and/or action.
To be clear, what you're doing here is giving the user an extension on their timeout if they initiate a call via Ajax. Once they refresh the page, or navigate away, they would be prompted to log back in, as normal.
Hope that helps. Let me know if you run into any issues.
Prior to making the Ajax call, can you make another one to an unauthorized controller to ensure that the user is authenticated? If they are, continue as normal, otherwise you can just show a login lightbox so you don't leave the page and maintain the user experience.
Another solution would be to add some script to your login page to check if it's being rendered within a pop-up. If it is you can use location.href() to redirect the whole page to the login page.
It depends a little on if you're ok with changing the length of time users will be logged in for. If you are, you can change your config file to something like the following...
<authentication mode="Forms">
<forms loginUrl="Login.aspx" timeout="512640" />
</authentication>
This will keep users logged in for one year. If changing the amount of time users are logged in is not an option, you would need to handle the ajax response and redirect users to a login form again.

Resources