MVC5 Web Application Scaffold - Account/Logoff - Why use HTTP Post? - asp.net-mvc

I am trying to get my head around MVC 5 Web Application template, and I noticed that special attention is given to the security around the LogOff link.
In the scaffold template the "LogOff" link in the _LoginPartial.cshtml view sits inside an HTML form with an AntiForgeryToken in it, and is defined as a JS call to form's submit action, like so:
#if (Request.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
</li>
<li>Log off</li>
</ul>
}
}
With the corresponding action method Account/LogOff inside ActionController defined like so:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
My question is - what is the reasoning behind it? Why does the LogOff action require so much security protection? Why not just have this in the view,
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
#Html.ActionLink("Log Off", "LogOff", "Account", routeValues: null, htmlAttributes: new { title = "LogOff" })
And this in the controller:
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
What security hole would this create?
Thanks.

Please refer to this link: Logout: GET or POST?.
It will answer your question on why Post should be used in logout.

Related

How to return user information to View ASP.net MVC

My view's code[_LoginPartial.cshtml] is like below. I want to show more details like department. How to do it? I succeed in getting departments in IdentityModels.cs. But I don't know how to use it in view.
IdentityModels.cs
var directoryEntry = new System.DirectoryServices.DirectoryEntry();
var directorySearcher = new System.DirectoryServices.DirectorySearcher(directoryEntry);
directorySearcher.Filter = string.Format("(&(objectClass=user)(SamAccountName={0}))", mADUser.SamAccountName);
var result = directorySearcher.FindOne();
var entry = result.GetDirectoryEntry();
var (string)mADUserDirectoryEntry.Properties["department"].Value;
_LoginPartial.cshtml
#using Microsoft.AspNet.Identity
#if (Request.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Login", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>
#Html.ActionLink("Hi" + User.Identity.GetUserName(), "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage", #style = "color: white" })
ログオフ
</li>
<li></li>
</ul>
}
}
Since your View Model is of type Microsoft.AspNet.Identity you only have properties available that comes out of the box. Unless you extend the class.
Here is how you can do that: How to extend available properties of User.Identity
Then you will be able to use the department data as User.Identity.Department or User.Identity.GetDepartment().

Register _LoginPartial if(Request.IsAuthenticated)

I'm hitting a dead end with this one. I'm new to MVC and I see similiar issue appearing on SO, but it does not help. I'm basically trying to set a Register action, and once user is registered _LoginPartial view should indicate that the user is authenticated. From various posts and articles I read I believe I'm missing the element of storing this user to the cookie, but I do not know how to achieve this. I will appreciate any hints.
Controler:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
int userId = WebSecurity.GetUserId(model.UserName);
string roleName = ConfigurationManager.AppSettings["CustomerRole"];
if (!Roles.RoleExists(roleName)){ Roles.CreateRole(roleName); }
Roles.AddUserToRole(model.UserName, roleName);
var customer = new Customer();
customer.UserId = userId;
customer.Name = model.Name;
customer.PrivateEmail = model.PrivateEmail;
_customerRepo.Add(customer);
_customerRepo.SaveChanges(); // Customer, Membership, UserProfile added to db, no problems
TempData["Message"] = "User was registered"; // shows
return RedirectToAction("Index", "Home"); // shows
It seems everything is being saved correctly but the partial view does not see this user anymore...
_LoginPartial view
#if(Request.IsAuthenticated) {
<text> user: #Html.ActionLink(User.Identity.Name, "Manage", "Account",routeValues: null, htmlAttributes: new { #class = "username", title = "Change Password" })
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{#Html.AntiForgeryToken()
Log off}
</text>
} else {
<ul>
<li>#Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
<li>#Html.ActionLink("Register", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}
You need to call Login() after the SaveChanges();
It should be something like
WebSecurity.Login(model.UserName, model.Password, false);
More information http://www.codeguru.com/csharp/.net/net_asp/mvc/using-simplemembership-in-asp.net-mvc-4.htm

MVC AuthenticationManager.SignOut() is not signing out

My project is based on the MVC 5 project template from Visual Studio 2013 (individual user account option). I have been relying on the default Sign In and Sign Out method for my users. But I'm not sure what I did, at some point, users cannot sign out anymore, however they can sign in as another user.
This is the default Logoff method of Account Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
This is the default _LoginPartial.cshtml View that shows user's username.
#using Microsoft.AspNet.Identity
#if (Request.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
</li>
<li>Log off </li>
</ul>
}
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>#Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
<li>#Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}
When user signs out, it directs user to the login page, but user's username is still shown which means that they have not signed out. And the url on the browser shows
http://localhost/Account/Login?ReturnUrl=%2FAccount%2FLogOff
It is not taking the user back to Index page of Home. So my guess is that something happened at the statement AuthenticationManager.SignOut();. I'm confused because I haven't changed anything to the Account Controller.
Any lead would be greatly appreciated.
I had the same problem.
Check this Issue on CodePlex:
http://web.archive.org/web/20160403071605/https://aspnetidentity.codeplex.com/workitem/2347
Try replacing AuthenticationManager.SignOut() with
AuthenticationManager.Signout(DefaultAuthenticationTypes.ApplicationCookie);
I hope that I help you. :-)
Simply add this line of code after SignOut():
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
Also check this out:
Page.User.Identity.IsAuthenticated still true after FormsAuthentication.SignOut()
I figured my problem was not at SignOut(). If you think your problem is at SignOut() (and using Owin Authentication) check out Sergio's link.
For my case is a stupid mistake!
I forgot I added [Authorize(Role = "admins")] to the controller, because I only wanted the admins to use the register method that comes with the default template. And the result is that no one except the admins can log out!
This is what I had:
[Authorize(Roles = "admin")]
public class AccountController : Controller
{
public ActionResult LogOff()
{
}
public ActionResult Register()
{
}
}
What I did is I just move the register methods to a new controller, like below:
[Authorize]
public class AccountController : Controller
{
public ActionResult LogOff()
{
}
}
and
[Authorize(Roles = "admin")]
public class AdminController : Controller
{
public ActionResult Register()
{
}
}
Now everyone can log out and only admins can register users.
(And the default AuthenticationManager.SignOut() works fine.)
Replace
AuthenticationManager.Signout();
with
AuthenticationManager.Signout(DefaultAuthenticationTypes.ApplicationCookie);
as per the issue in Sergio's answer
I have faced with the same problem..
Below is a standard ASP.Net MVC logOff procedure presented by Microsoft and it works, except user after logoff and login you will get wrong, It happens because authentication token. And as soon as you will release that it simple to modify. But nobody says about that.
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
As you can see that the standard way of logoff in AccountController
That the html code, with my little hack -> id="logoffbtn"
#using (Html.BeginForm("LogOff", "Account"))
{
#Html.AntiForgeryToken()
<button class="btn btn-default btn-flat" id="logoffbtn"
type="submit">Logout</button>
}
So solution to properly logoff using standard way is to add somewhere in your main java script file little hack:
$(document).ready(function()
{
try
{
$('#logoffbtn').click(function ()
{
// alert('Sign new href executed.');
console.log("Session token start");
sessionStorage.removeItem('accessToken');
console.log("Session token done");
});
} catch (e)
{
console.log("Session token err");
}
});
So what will happen next..basically after pressing logoff button you will clear access Token and now logoff will start working normally.
Hope that helps.
P.S. I have tried really many ways and only this was a hack for me.

MVC Area - How to Link to Root folder in BeginForm

I need to define a link to LogOff controller which is in my Root shared folder; within BeginForm tag.
#using Microsoft.AspNet.Identity
#if (Request.IsAuthenticated) {
using (Html.BeginForm("", "", FormMethod.Post, new { id = "logoutForm", action = "Account/LogOff"}))
{
#Html.AntiForgeryToken()
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
#: | Log off
}
}
else {
#Html.ActionLink("Register", "Register", "Account")
#: |
#Html.ActionLink("Login", "Login", "Account")
}
Above works fine if I'm in root folder. But if clicked from Areas, it gives me The resource cannot be found error.
Requested URL: /MyApp/Area/Account/LogOff
The correct link should be /MyApp/Account/LogOff
I saw examples using #HTML.ActionLink but would prefer to keep define it in BeginForm, so the URL is not revealed to user.
I solved the problem with the following code.
First I mapped a route as follows
//Route config for logging off from areas.
routes.MapRoute(
name: "LogOff",
url: "Account/LogOff/",
defaults: new { controller = "Account", action = "LogOff" }
);
Then calling the route to logout, I used the following
#using (Html.BeginRouteForm("LogOff", FormMethod.Post, new { id = "logoutForm" })) {
#Html.AntiForgeryToken()
Log off
}
It may be easier to just add the Area routevalue to the BeginForm parameters. Leave it blank to point it to the root area, like this: new { Area = "" }
using (Html.BeginForm("LogOff", "Account", new { Area = "" }, FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>Log off</li>
</ul>
}
This is using MVC 5

MVC "LogOut" controller causes "The resource cannot be found." error when using "ValidateAntiForgeryToken" attribute

When I add "ValidateAntiForgeryToken" attribute to my LoggOff controller, it doesn't map my controller and raises "The resource cannot be found." error. What is the problem? Here is my controller:
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
Here is my view:
#using Microsoft.AspNet.Identity
#if (Request.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "__RequestVerificationToken", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>#Html.ActionLink("User: " + User.Identity.GetUserName(), "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })</li>
<li>#Html.ActionLink("Log Off", "LogOff", "Account")</li>
</ul>
}
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>#Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
<li>#Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}
you are not posting the form when you log off
this line will create a GET request
<li>#Html.ActionLink("Log Off", "LogOff", "Account")</li>
you need to add a submit button eg
Logout
Test this solution web.config, I had this problem and I had forgotten to uncomment tags

Resources