I have an ASP.NET MVC application and I have received complaints from users that they are getting logged out after a relatively short time.
I have the following settings in web.config but apparently it does not work.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
Any idea why this is not working?
I believe you have same issue as here
So you should place machine key in your Web.Config. Ex:
<configuration>
<system.web>
<machineKey decryptionKey="F6722806843145965513817CEBDECBB1F94808E4A6C0B2F2,IsolateApps" validationKey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45,IsolateApps" />
</system.web>
</configuration>
You can learn how to generate machine key here or here
If you want user stays login until his/her browser is open, I do a trick. I create an Action or any Handler like below, it just return some dummy text (ex: context.User.Identity.IsAuthenticated), and then use jQuery.get to request this. I keeps user login because browser keep sends requests:
Handler:
public class KeepAlive : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write(context.User.Identity.IsAuthenticated);
}
public bool IsReusable
{
get
{
return false;
}
}
}
MVC Action:
public bool KeppChecking()
{
return User.Identity.IsAuthenticated;
}
jQuery:
setInterval(function () {
$.get('/Ajax/KeepAlive.ashx');
}, 10000);
Related
I want to always redirect to my home page if someone puts something like www.example/bla/qwerty.hm (it doesnt have controller and it doesnt exist) to www.example.com.
I want to redirect all urls, which have not controller, to my home page and not show error. For example: www.example.com/auto (auto is not controller) and it will be redirect to my home page. How do I do that?
I tried Route configuration
routes.MapRoute(
name: "MyRedirect"
, url: "{contextRedirect}/{*tasks}"
, defaults: new { controller = "Redirect", action = "TotalRedirect", contextRedirect = "" }
);
...
public ActionResult TotalRedirect(string contextRedirect)
{
return RedirectToAction("Index", "Home");
}
but this is called every time and it makes an infinity loop (it is called every time when redirectToAction is called)
The problem disappears if I write all RouteMaps for all existing controllers before this MapRoute, but I have lot of controllers, and I want avoid writing RouteMaps for all controllers.
Instead of MapRoute I tried Web.config and change errors
<customErrors mode="On">
<error redirect="/Error" statusCode="404" />
<error redirect="/Error" statusCode="500" />
</customErrors>
Error is controller which return RedirectToAction and I got same result as point 1 (infinity loop). But when I change /Error to /Home it is working (because Home return View), but on Url path is saved error text Home?aspxerrorpath=/auto. After redirect I do not want show text, that means if page will be redirected in to www.example.com, it not show www.example/Home?aspxerrorpath=/auto.
I am new to Asp.net MVC, and I don't know proper way how to do it.
Update
After some research I think there are two ways how to do it.
(Thanks to KevinLamb) Redirect from error with Application_Error and Web.Confing httpError. This is working for me:
This settings is put in Web.Confing on Project level, that means first Web.Config you see in Project Explorer (there is second Web.Config on View folder). Then you must create Controller named Error with ActionResult ErrorHandle
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
<remove statusCode="400"/>
<error statusCode="400" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
<remove statusCode="500"/>
<error statusCode="500" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
</httpErrors>
...
// Error Controller .cs
namespace MyWebApp.Controllers
{
public class ErrorController : Controller
{
// GET: Error
public ActionResult Index()
{
return RedirectToAction("Home", "Index");
}
public ActionResult ErrorHandle()
{
return RedirectToAction("Index", "Home");
}
}
}
...
// code inside Global.asax.cs MvcApplication class
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//Add some logging here
if(ex.GetType().IsAssignableFrom(typeof(HttpException)))
{
//Possibly log that you're redirecting the user
Response.Clear();
Response.Redirect("~/");
}
}
This is easy part.
Another way I discover is create HttpHandler or HttpModule. I am new in MVC and Asp.Net world and HttpHandler is not working allways for me, because it works only once, then only when application change page, but it not detect Url created by user (only first time). HttpModule work allways for me, but I don't know if it is good or bad. It is little harder then 1. point but you don't need Application_Error and httpErrors in Web.Config.
If you have httpErrors and Application_Error, delete it and create Module (Right click on Project > Add new Item > In search put "module" > and select Asp.Net Module. It create module class with Init and Dispose methods. Then create your own method and register it to BeginRequest.
And here is code for my HttpModule
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using System.Linq;
namespace MyWebApp
{
public class ErrorHttpModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
// Below is an example of how you can handle LogRequest event and provide
// custom logging implementation for it
// context.LogRequest += new EventHandler(OnLogRequest);
context.BeginRequest += new EventHandler(BR); // register your own method in to Event where you check Url
}
#endregion
private HttpContext context = null;
public void BR(Object source, EventArgs e)
{
context = System.Web.HttpContext.Current;
// collect all controllers in web application
Assembly asm = Assembly.GetAssembly(typeof(MyWebApp.MvcApplication)); // need using System.Reflection;
var controlleractionlist = asm.GetTypes()
.Where(type => typeof(System.Web.Mvc.Controller).IsAssignableFrom(type)) // need using System.Linq;
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
.Select(x => new { Controller = x.DeclaringType.Name, Action = x.Name, ReturnType = x.ReturnType.Name, Attributes = String.Join(",", x.GetCustomAttributes().Select(a => a.GetType().Name.Replace("Attribute", ""))) })
.OrderBy(x => x.Controller).ThenBy(x => x.Action).ToList();
// Get Url
string page = "";
if (context != null)
{
page = context.Request.Url.PathAndQuery;
}
string newUrl;
if (!String.IsNullOrEmpty(page))
{
bool continute = true;
// does url contain controller or action?
foreach (var controller in controlleractionlist)
{
string cpath = "/" + controller.Controller.Replace("Controller", "") + (controller.Action == "Index" ? "" : "/" + controller.Action);
if (cpath == page)
{
// Yes, don't continue to redirect
continute = false;
break;
}
else if (page == ("/" + controller.Action))
{
// Yes, don't continue to redirect
continute = false;
break;
}
}
// does page load your content, script etc.. ?
if (page.Contains("Content/") == true
|| page.Contains("Scripts/") == true
|| page.Contains(".ico") == true
|| page == "/"
)
{
// Yes, don't redirect.
continute = false;
}
if (continute)
{
// anything else will redirect to Home page
var urlHelper = new UrlHelper(context.Request.RequestContext); // nned using System.Web.Mvc;
newUrl = urlHelper.Action("About", "Home");
context.Response.Status = "301 Moved Permanently";
context.Response.AddHeader("Location", newUrl);
context.Response.End();
}
}
}
public void OnLogRequest(Object source, EventArgs e)
{
//custom logging logic can go here
}
}
}
And finnaly add module in to Web.Config (on Project level (ProjectName > Web.Config), not inside folder under Project (ProjectName > View > Web.Config))
<system.webServer>
<modules>
<add name="MyHttpErrorModule" type="MyWebApp.ErrorHttpModule, MyWebApp"/>
</modules>
</system.webServer>
Extended question
Either first or second point, I have problem with both, when in url is put characters like that /abc=45%$#r. It leads to Bad Request - Invalid URL HTTP Error 400. The request URL is invalid. and this don't detect Application_Error, httpErrors in Web.Config or my HttpModule with BeginRequest. Therefore I think this is on IIS settings but what I need to set up? It looks like those characters make IIS mess %$.
First you'll need to set errors to pass through in IIS so that your application can handle the file/parameter links instead of IIS. Add the below to your Web.config:
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
If you want to redirect to your home page (without any extra parameters in the URL) when you get an issue with a user going to the wrong controller/action, add the following code to your Global.asax.cs.
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//Add some logging here
if(ex.GetType().IsAssignableFrom(typeof(HttpException)))
{
//Possibly log that you're redirecting the user
Response.Clear();
Response.Redirect("~/");
}
}
Basically, what this is is doing is making all errors in your ASP.NET MVC website go through this method. Then it checks to see if the exception is a HttpException. If it is, it will redirect to your home controller and action. If it is not a HttpException it will continue doing the error handling process. This way you can keep handling the important errors properly.
I am trying to follow some instructions I found online in order to implement output caching for episerver.
In my web.config I have set up the following:
<caching>
<outputCache enableOutputCache="true">
</outputCache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="ClientResourceCache" enabled="true" duration="3600" varyByParam="*" varyByContentEncoding="gzip;deflate" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
As a test, I selected StartPageController.cs and added the [ContentOutputCache] tag like so:
[ContentOutputCache]
public class StartPageController : PageControllerBase<StartPage>
{
public ActionResult Index(StartPage currentPage)
{
var model = PageViewModel.Create(currentPage);
if (SiteDefinition.Current.StartPage.CompareToIgnoreWorkID(currentPage.ContentLink))
{
var editHints = ViewData.GetEditHints<PageViewModel<StartPage>, StartPage>();
editHints.AddConnection(m => m.Layout.Logotype, p => p.SiteLogotype);
editHints.AddConnection(m => m.Layout.Footer, p => p.FooterBlock);
}
return View(model);
}
}
}
Then the instructions say:
At some point on a page load you need to add the following code:
public void SetResposneHeaders()
{
HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.AddMinutes(2.0));
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
}
Due to my limited knowledge of .NET, MVC etc.. this part confuses me as I am not sure in which file to place it and where? Does this go in StartPageController.cs? Or somewhere else?
Any pointers would be appreciated.
The instructions I am trying to follow are here.
I have a simple MVC project that uses forms authentication. When user click login button, the code section below in the controller has being called:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> OturumAc(LoginViewModel model, string returnUrl)
{
//OturumAcmaSonuc = ResultOfLogin, sonuc = result
OturumAcmaSonuc sonuc = null;
//Kullanici = User
Kullanici kullanicisi = null;
//Anasayfa = MainPage
AnasayfaViewModel anasayfaModel = null;
PersonelViewModel personelvm = null;
if (!ModelState.IsValid)
{
return View(model);
}
try
{
personelvm = new PersonelViewModel();
//AcOturum = Login
sonuc = this._personelik.AcOturum(model);
// OturumAcmaSonucu = CanSheLogin
if (sonuc.OturumAcmaSonucu)
{
FormsAuthentication.SetAuthCookie(sonuc.Kullanicisi.KullaniciAdi, false);
kullanicisi = sonuc.Kullanicisi;
//Depola = Store
this._cache.Depola(CacheDepolamaEnum.Kullanici, kullanicisi);
anasayfaModel = new AnasayfaViewModel(sonuc.Mesaj, kullanicisi);
//Depola = Store
this._cache.Depola(CacheDepolamaEnum.AnasayfaViewModel, anasayfaModel);
//Anasayfa = MainPage
return RedirectToAction("Anasayfa", "Account");
}
else
{
TempData["Hata"] = SistemMesajlar.BasarisizOturumAcma();
}
}
catch (Exception Hata)
{
this._hataik.YazHata(Hata);
}
return View(model);
}
This action method shall be the ultimate method for login. As you expected, when the result of login is true, the execution is expected to branch out to "Anasayfa" (MainPage) action method, however it branch out to "Login" action method. This is the error. Here is the content of the web.config of the web layer:
<system.web>
<globalization uiCulture="tr-TR" culture="tr-TR" />
<compilation debug="true" targetFramework="4.5.2" />
<authentication mode="Forms">
<forms loginUrl="/Account/OturumAc" timeout="20" slidingExpiration="true" />
</authentication>
<customErrors defaultRedirect="/Error" mode="On">
</customErrors>
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
when I write the "Login" action method as described below, It branch out to Login.cshtml:
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
When I remove the [AllowAnonymous] tag, it gets "The request filtering module query string is configured to be too long." the requested url is : " http://localhost:29736/Account/Login?ReturnUrl=%2FAccount%2FLogin%3FReturnUrl%3D%252FAccount%252FLogin%253FReturnUrl%253D%25252FAccount%25252FLogin%25253FReturnUrl%25253D%2525252FAccount%2525252FLogin%2525253FReturnUrl%2525253D%252525252FAccount%252525252FLogin%252525253FReturnUrl%252525253D%25252525252FAccount%25252525252FLogin%25252525253FReturnUrl%25252525253D%2525252525252FAccount%2525252525252FLogin%2525252525253FReturnUrl%2525252525253D%252525252525252FAccount%252525252525252FLogin%252525252525253FReturnUrl%252525252525253D%25252525252525252FAccount%25252525252525252FLogin%25252525252525253FReturnUrl%25252525252525253D%2525252525252525252FAccount%2525252525252525252FLogin%2525252525252525253FReturnUrl%2525252525252525253D%252525252525252525252FAccount%252525252525252525252FLogin%252525252525252525253FReturnUrl%252525252525252525253D%25252525252525252525252FAccount%25252525252525252525252FLogin%25252525252525252525253FReturnUrl%25252525252525252525253D%2525252525252525252525252FAccount%2525252525252525252525252FLogin%2525252525252525252525253FReturnUrl%2525252525252525252525253D%252525252525252525252525252FAccount%252525252525252525252525252FLogin%252525252525252525252525253FReturnUrl%252525252525252525252525253D%25252525252525252525252525252FAccount%25252525252525252525252525252FLogin%25252525252525252525252525253FReturnUrl%25252525252525252525252525253D%2525252525252525252525252525252FAccount%2525252525252525252525252525252FLogin%2525252525252525252525252525253FReturnUrl%2525252525252525252525252525253D%252525252525252525252525252525252FAccount%252525252525252525252525252525252FLogin%252525252525252525252525252525253FReturnUrl%252525252525252525252525252525253D%25252525252525252525252525252525252FAccount%25252525252525252525252525252525252FLogin%25252525252525252525252525252525253FReturnUrl%25252525252525252525252525252525253D%2525252525252525252525252525252525252FAccount%2525252525252525252525252525252525252FLogin%2525252525252525252525252525252525253FReturnUrl%2525252525252525252525252525252525253D%252525252525252525252525252525252525252FAccount%252525252525252525252525252525252525252FAnasayfa"
I try to find two solutions :
1-) Whenever a user successfully logins, it shall branch out to "Anasayfa" (MainPage) action method.
1-) Execution shall not branch out to "Login" action method, it shall branch out "OturumAc" action method.
How can I overcome these? Thanks in advance.
Lets say that you have websites http://simple.com and a lot of subdomain, for example: http://first.simple.com, http://second.simple.com, http://last.simple.com.
Lets say that a user goes to last.simple.com and they get authenticated through the normal ASP .NET membership provider.
Then, from that site, they get sent to (redirection, linked, whatever works) site http://last.simple.com, and the intent of site http://first.simple.com was to pass that user to the other site as the status of isAuthenticated, so that the site http://last.simple.com does not ask for the credentials of said user again.
This worked in Chrome, Mozila, Opera(last versions), Safari, but don't worked in IE(all versions) and Opera( < v12.01).
Scheme:
User, address(first.simple.com) -> post query to server, json answer,
if auth - redirect. May be problem in json(need use 'jsonp')?
web.config
<system.web>
<authentication mode="Forms">
<forms name=".ASPXAUTH" protection="All" domain=".simple.com" enableCrossAppRedirects="true" />
</authentication>
</system.web>
SessionService
public void Authenticate(string username)
{
FormsAuthentication.SetAuthCookie(username, false);
var cookie = FormsAuthentication.GetAuthCookie(username, false);
cookie.HttpOnly = true;
cookie.Path = "/";
cookie.Domain = domain;
this.context.Response.AppendCookie(cookie);
}
Global.asax
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
const string aspSessionid = "ASP.NET_SessionId";
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
{
var cookie = Context.Request.Cookies[aspSessionid];
if (cookie != null && Context.Session != null && !string.IsNullOrEmpty(Session.SessionID))
{
Response.Cookies.Add(new HttpCookie(aspSessionid, Session.SessionID) { Domain = domain, Path = "/", Expires = DateTime.Now.AddDays(30) });
}
}
}
I am trying to implement LDAP authentication on the ADAM LDAP server of my company with ASP.NET MVC.
I already tested coordinates and credentials with other LDAP clients and they work fine.
The server is not SSL secured and it works on standard 389 port, so I set connectionProtection="None" in web.config membership provider.
When I try login with default login page, I get this error: "Default credentials are not supported when the connection protection is set to None".
Here are parts of web.config
<configuration>
<connectionStrings>
<add name="ADConnectionString" connectionString="LDAP://mycompanyldapserver.com:389"/>
....
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionProtection="None"
attributeMapUsername="sAMAccountName"
enableSearchMethods="True"/>
</providers>
</membership>
AccountController
[AllowAnonymous]
[HttpPost]
public JsonResult JsonLogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// >> Membership.ValidateUser THROWS EXCEPTION AFTER LOGIN TRY >>
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Json(new { success = true, redirect = returnUrl });
}
EDIT
I discovered that the exception "Default credentials are not supported when the connection protection is set to None" is not thrown when I add these parameters to DefaultMembershipProvider:
connectionUsername="username"
connectionPassword="userpwd"
The problem is that username and password must change runtime, based on login form...may you help me to set these parameters runtime, please?
I tried to implement this solution, which I suppose could work...I suppose because now I get a "wrong username or password" error instead of above exception specified in the subject. I think I am a step away to solve this problem, anyway I need help from some expert. I do not konw if the following solution can work properly to validate user:
MODIFIED ACCOUNT CONTROLLER: I AM TRYING TO ADD MEMBERSHIP PROVIDER RUNTIME
[AllowAnonymous]
[HttpPost]
public JsonResult JsonLogOn(LogOnModel model, string returnUrl)
{
// I'm trying to add membership provider runtime...
System.Web.Security.ActiveDirectoryMembershipProvider mprov = new System.Web.Security.ActiveDirectoryMembershipProvider();
NameValueCollection objConfig = new NameValueCollection();
objConfig.Add("name", "DefaultMembershipProvider");
objConfig.Add("type", "System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
objConfig.Add("connectionStringName", "ADConnectionString");
objConfig.Add("connectionProtection", "None");
objConfig.Add("connectionUsername", model.UserName);
objConfig.Add("connectionPassword", model.Password);
objConfig.Add("attributeMapUsername", "sAMAccountName");
objConfig.Add("enableSearchMethods", "True");
try
{
// Here I get "wrong username or password error...wrong DN base? I do not know.....
mprov.Initialize(objConfig["name"], objConfig);
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Json(new { success = true, redirect = returnUrl });
}
catch (System.Runtime.InteropServices.COMException ex)
{
// For now I am catching all COMExceptions...if it works I could change behavior...
ModelState.AddModelError("", ex.Message);
}
Thanks in advance for your precious help.
I succeed to get it work by the following way. I am newbie and probably it's not the best soluton, so any further hint or suggestion is welcome and appreciated!
The main problem was the following error: "Default credentials are not supported when the connection protection is set to None". When DefaultMembershipProvider has connectionProtection="None" it needs connectionUsername and connectionPassword to be set:
web.config
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionProtection="None"
connectionUsername="username"
connectionPassword="userpwd"
attributeMapUsername="sAMAccountName"
enableSearchMethods="True"/>
</providers>
</membership>
The second problem was that I use LDAP for authentication, so I did not want to use static connectionUsername and connectionPassword. So I decided to define whole MembershipProvider runtime, modifying my account controller JsonLogOn action (LogOn action should be modified in similar way):
ACCOUNT CONTROLLER / LOGON
// POST: /Account/JsonLogOn
[AllowAnonymous]
[HttpPost]
public JsonResult JsonLogOn(LogOnModel model, string returnUrl)
{
System.Web.Security.ActiveDirectoryMembershipProvider mprov = new System.Web.Security.ActiveDirectoryMembershipProvider();
string baseDN = "OU=myou,OU=myou1,O=myO";
NameValueCollection objConfig = new NameValueCollection();
objConfig.Add("name", "DefaultMembershipProvider");
objConfig.Add("type", "System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
objConfig.Add("connectionStringName", "ADConnectionString");
objConfig.Add("connectionProtection", "None");
objConfig.Add("connectionUsername", "uid=" + model.UserName + "," + baseDN);
objConfig.Add("connectionPassword", model.Password);
objConfig.Add("attributeMapUsername", "sAMAccountName");
objConfig.Add("enableSearchMethods", "True");
try
{
mprov.Initialize(objConfig["name"], objConfig);
}
catch (System.Runtime.InteropServices.COMException ex)
{
switch (ex.ErrorCode)
{
// Server give back a value...
case -2147016661:
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Json(new { success = true, redirect = returnUrl });
}
}
ModelState.AddModelError("", ex.Message);
}
// If we got this far, something failed
return Json(new { errors = GetErrorsFromModelState() });
}
Et voilĂ ! It works fine, at least for my needs ! :)
I hope this can help someone else...pls vote up if you find this solution useful, thx. I spent some hour to find it... did not find anything ready on the net.
Thank you!
Best Regards