My MVC application uses FormsAuthentication to sign in.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(FiNext.Models.User user)
{
try
{
using (HttpClient httpClient = new HttpClient())
{
var task = httpClient.PostAsJsonAsync<FiNext.Models.User>(String.Concat(ServiceUri.ApiUrl,"/Test/Validate"), user).Result;
FormsAuthentication.SetAuthCookie(user.Name, false);
SessionHelpers.UserId = user.Id;
return RedirectToAction("Create");
}
}
And it has a Session time out of 1 minute(in web.config) and once the session time out is called ,I am clearing sessions in session_end event in Global.asax.
protected void Session_End(object sender, EventArgs e)
{
Session.Clear();
Session.Abandon();
}
Now the problem when i sign out using normal log off button on the page,the page gets signed out.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
try
{
FormsAuthentication.SignOut();
return RedirectToAction("Home", "User");
}
catch (Exception ex)
{
throw ex;
}
}
and now i hit any url of this application(say "http://abcd.com/User/UserList") it is redirected to login page as we have logged out and redirecting to home page.
This is the desired functionality and working fine.
But the problem is when there is session time out and session_end event is fired.And now when i hit any url of this application(say "http://abcd.com/User/UserList"),iam able to get the data(which should not happen).
So how to signout from forms authentication when session_end is fired.
I tried this in session_end event in Global.asax:
protected void Session_End(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Session.Clear();
Session.Abandon();
}
but its gives "Object reference not set to an instance of an object." exception.
Maybe I am missing something, but it sounds like an authorization problem, not a session problem.
Is your UserList action secured in some way?
[Authorize]
public ActionResult UserList()
{
return View();
}
http://msdn.microsoft.com/en-us/library/ff398049(v=vs.100).aspx
Related
I am using ASPNet Identity 2.0 (Full framework, not the core framework) and MVC.
I would like to execute C# code once the user successfully login to the site.
i know that i can write some code right after the SignInManager.PasswordSignInAsync command and it will work for new login but not will not work for users who used "remember me" feature and returned to the site later (Cookie authentication).
I am looking for an option to catch the event of all the users who signed in to the site either by entering the password and by using the "remember me" cookie.
One way you can do it is by handling the application event in your global.asax file.
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
Response.Write("HELLO");
}
}
There are many ways to do it. You can create and use custom attribute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
readonly IAuthentication _authentication;
public CustomAuthorizeAttribute(IAuthentication authentication)
{
_authentication = authentication;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!_authentication.Authorize(filterContext.HttpContext))
filterContext.Result = new HttpUnauthorizedResult();
//your code .....
}
}
And now rather than using [Authorize] use your new [CustomAuthorizeAttribute] attribute
[CustomAuthorizeAttribute]
public ActionResult Index()
{
ViewBag.Title = "Welcome";
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
What you're after is FormsAuthentication_OnAuthenticate (I appreciate Forms-based authentication was not mentioned in the question, but it's an example of Cookie-based remember me authentication, adapt at will)
Unfortunately, there is no trigger for OnCookieBasedFormsAuthenticate_SessionCreate :)
So what you can do is check the Forms-based authentication every so often, because it (and Application_AuthenticateRequest) is fired for every request, CSS pages, images etc, going off to the database to check multiple times per request is an overly resource hungry idea. Luckily the forms cookie ticket has an issued on date and we can use that to check:
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
if ((DateTime.Now - ticket.IssueDate).TotalMinutes > 10)
{
if(/*Insert logic to check username here with ticket.Name*/)
{
//recreate cookie with new issuedate
FormsAuthentication.SetAuthCookie(ticket.Name, ticket.IsPersistent);
}
else
{
FormsAuthentication.SignOut();
}
}
Debug.WriteLine($"{ticket.Name} {ticket.IssueDate.ToUniversalTime()}");
}
catch (Exception e)
{
//Elmah
ErrorSignal.FromCurrentContext().Raise(e);
//Cannot decrypt cookie, make the user sign in again
FormsAuthentication.SignOut();
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not supported for this application.");
}
}
This is not the same as cookie expiration, because the user will still retain login status; the user should never see any login request unless there account is no longer valid.
We are developing an ASP.NET MVC web application using .NET Framework 4.5 and claims-based authentication/authorization. The application uses the standard ASP.NET mechanism to save a token into a cookie to read claims between POSTs that have been previously cached into memory.
We have configured the session timeout in the web.config file like this:
<sessionState timeout="1" mode="InProc"/>
When a session timeout occurs, the Session_End event handler is called:
protected void Session_End(object sender, EventArgs e)
{
Session.RemoveAll();
FederatedAuthentication.SessionAuthenticationModule.SignOut();
}
After Session_End is executed, the Session_Start handler is triggered:
protected void Session_Start(object sender, EventArgs e)
{
Breadcrumb breadcrumb = new Breadcrumb();
Session["Breadcrumb"] = breadcrumb;
}
This behavior causes a custom filter checking whether there is session or not to return always true:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class SessionExpireFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Get context
HttpContext ctx = HttpContext.Current;
// If the browser session has expired...
if (ctx.Session != null && ctx.Session["Breadcrumb"] == null)
{
// Redirect to login page
}
}
}
On the other hand, if the user clicks the "Log out" menu option, the following handler is called:
public ActionResult Logout()
{
FederatedAuthentication.SessionAuthenticationModule.SignOut();
return RedirectToAction("Login", "Account");
}
After this, if we enter a valid application URL into the browser, the page is shown without being prompted for credentials. Does this mean that the token is still valid?
Could you please tell us what is happening?
Thanks very much in advance for your help.
I'm attempting to create a DotNetOpenAuth OpenID MVC 3 project but am unable to receive a users authentication status. My post method works fine but when the request returns to the HttpGet method, and I check the User.Identity.IsAuthenticated status it returns false when I have logged in successful to any openID provider.
I have enabled communication with localhost in the webconfig dotNetOpenAuth untrustedWebRequest which hasn't solved the problem, and I have spent hours searching for an answers to why a user is not being returned as an authenticated user.
There must be some logic error that would cause a user to not be returned form an openID provider as authenticated in my code but I can't find it.
Thanks for any response to my question!
My controller:
namespace DotNetOpenAuth_OpenID.Controllers
{
public class UserController : Controller
{
private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
public IFormsAuthenticationService FormsService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (FormsService == null)
{
FormsService = new AuthenticationService();
}
base.Initialize(requestContext);
}
// **************************************
// URL: /User/LogIn
// **************************************
[HttpGet]
public ActionResult LogIn()
{
if (User.Identity.IsAuthenticated) <===== RETURNS FALSE
{
return RedirectToAction("Profile", "User");
}
Identifier id;
if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
{
try
{
var req = openid.CreateRequest(Request.Form["openid_identifier"]);
return req.RedirectingResponse.AsActionResult();
}
catch (ProtocolException ex)
{
//display error by showing original LogOn view
//this.ErrorDisplay.ShowError("Unable to authenticate: " + ex.Message);
return View("Login");
}
//return LogIn(new User { OpenID = id }, Request.Form["ReturnUrl"]);
}
return View("Login");
}
[HttpPost]
public ActionResult LogIn(User model, string returnUrl)
{
string openID = ModelState.IsValid ? model.OpenID : Request.Form["openid_identifier"];
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Profile", "User");
}
else if (!string.IsNullOrEmpty(openID))
{
return Authenticate(openID, returnUrl);
}
else if (ModelState.IsValid)
{
ModelState.AddModelError("error", "The OpenID field is required.");
}
// If we got this far, something failed, redisplay form
return View(model);
}
I was following an example that can be found at http://codesprout.blogspot.com/search/label/openid, and had not implemented all the code yet, which was why a user was never returned authenticated. I was missing the call to var response = openid.GetResponse() that was within another method called Authenticate in the controller. Another source for the entire code can be found at a Stack Overflow post here What OpenID solution is really used by Stack Overflow?
I'm working with ASP.NET MVC 4. When we build a project, they add an AccountController by default. You can create an acoount and log in right away... but after they've logged in, try to type http://localhost:port/Account/LogOn. You still stay at the Login page with the login form.
I have tried to redirect the user by using this code:
//
// GET: /Account/LogOn
[AllowAnonymous]
public ActionResult LogOn()
{
if(Membership.GetUser().UserName != null)
RedirectToAction("Index", "Home");
return ContextDependentView();
}
The code works fine until I log out, and then this code no longer works. Please show me the right way to do this.
Fixed:
//
// GET: /Account/LogOn
[AllowAnonymous]
public ActionResult LogOn()
{
if(User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
else
{
return ContextDependentView();
}
}
You don't have to copy over the rights-chechking code - do write a filter instead and use it multiple times with no code duplication.
public class GuestsOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting( ActionExecutingContext filterContext )
{
if( User.Identity.IsAuthenticated )
{
filterContext.Result = new ViewResult { ViewName = "RestrictedArea" };
}
}
}
In your controller
// GET: /Account/LogOn
[GuestsOnly]
public ActionResult LogOn() { ... }
i have created the controller :
[Authorize]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
try
{
db.DeleteObject(db.AEROLINEA.FirstOrDefault(x => x.AEROLINEAID == id));
db.SaveChanges();
}
catch { /* TODO:Display message*/ }
return View();
}
if i execute in firebug the next javascript anyone logged could delete an airline even if he doesnt have permissions to delete
var action = "/Airline/Delete/" + recordId;
var request = new Sys.Net.WebRequest();
request.set_httpVerb("DELETE");
request.set_url(action);
request.add_completed(deleteCompleted);
request.invoke();
HOw can avoid this issue???
You can filter the the roles:
Example:
[Authorize(Roles="Admin")]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
try
{
db.DeleteObject(db.AEROLINEA.FirstOrDefault(x => x.AEROLINEAID == id));
db.SaveChanges();
}
catch { /* TODO:Display message*/ }
return View();
}
Or use the AntiforgeryToken with a juicy salt at the View..
[Authorize] without parameters allows you to indicate that a user must be logged in. You also can specify users/roles, authorized to access your action