I am trying to keep track of something and using the SessionID as they key to that object
However the SessionID every 2-3 reqiests changes shouldn't it remain the same?
HttpContext.Session.SessionID
Is the code I am using.
I've seen that happen even without MVC. If I remember correctly, ASP.NET keeps assigning new session ids until you place something into the Session variable.
You should initialize the Session object within Global.asax.cs.
void Session_Start(object sender, EventArgs e)
{
HttpContext.Current.Session.Add("__MyAppSession", string.Empty);
}
This way the session will not change unless your browser window is closed.
I am working on a .NET MVC cart application and I added
Session["myVar"] = "1234";
to the Page_Load method found in the Default.aspx.cs code. I also added
<%= this.Session.SessionID %>
to the Site.Master footer. I then rebuilt my app and browsed the various pages of my app and the footer displays the same session id for all pages as expected!
If you look the seession ID cookie is not even sent to the browser unless it's used on the server.
So when a page roundtrips there is no current session ID cookie, so a new session ID is created, hence it is random.
This is logical, why bother tying up the app to a session if the session is not in use?
I would look into using TempData to keep track of something.
Try with adding machine key into your web.config:
Online key generator: http://aspnetresources.com/tools/keycreator.aspx
It seems that server resets machine key for client and generates new one with new session id, every few minutes, which is much lower than it should. Don't know if that's bug or feature:)
Also, you can increase your session state timeout, which is i think 20min by default.
I was having the same problem using ASP.NET Web Forms. I just had to add a global.asax file to the solution and the fixed it for me.
Summing up answers from #jrojo and #Maxam above, with what I am using.
I am using AWS DynamoDB as the session store (out of scope of the question a little, but gives sample).
Add package via NUGET:
Install-Package AWS.SessionProvider
Update Web.config to have keys in appSettings:
<add key="AWSAccessKey" value="XXX" />
<add key="AWSSecretKey" value="YYY" />
And session provider to system.web:
<sessionState timeout="20"
mode="Custom"
customProvider="DynamoDBSessionStoreProvider">
<providers>
<add name="DynamoDBSessionStoreProvider"
type="Amazon.SessionProvider.DynamoDBSessionStateStore, AWS.SessionProvider"
AWSProfilesLocation=".aws/credentials"
Table="ASP.NET_SessionState"
Region="us-east-1"
/>
</providers>
</sessionState>
Add anything to session in global.asax on session start:
void Session_Start(object sender, EventArgs e) {
HttpContext.Current.Session.Add("somethingToForceSessionIdToStick", string.Empty);
}
Verify by adding this to razor of any page. Refresh that page, then open an ignito window and see a different session:
#HttpContext.Current.Session.SessionID
BobsYourUncle
please see if you have set the cookie samesite attribute to strict.
remove cookieSameSite="Strict" and check.
Related
I want to set FormsAuthentication cookie name dynammically, for example a guid. how can i do that. I can already change it to whatever I want in web.config. I just can't do it in code and dynamically.Please help.
<authentication mode="Forms">
<forms name="myName" loginUrl="~/Account/Login" defaultUrl="~/Admin/admin" cookieless="UseCookies" slidingExpiration="true" timeout="40320"/>
</authentication>
The reason that I want to do this is, i have several instances of my application on the same host and i do not want them to overwrite each other's cookies.
I have been struggling with Cookies with quite a few days. It has been an awesome learning experience.
So wanted to share the possible ways I found & discovered: There are several HACKs to modify Forms Authentication Cookie name:
You can automate the modification of cookie name under Authenticaiton secion of Web.Config file in Application_Start event in Global.asax. Thanks to Ron for sharing this. But I could not guarantee that the user whose identity would be used to run application domain have enough privileges to modify the file on disk or not. Hence I needed an improvised solution, so I devised following.
Thanks to ILSpy for letting me see inside the FormsAuthentication class, and many thanks to Reflection to let me modify the private field of a class. I used following code to modify the cookie name on run-time with following small piece of code and this worked like a charm !!!
protected void Application_Start(Object sender, EventArgs e)
{
// This will enforce that FormsAuthentication class is loaded from configuration settings for the application.
FormsAuthentication.Initialize();
// The new cookie name whatever you need can go here, I needed some value from my application setting to be prefixed so I used it.
string newCookieName = string.Format("{0}.ASPXAUTH", ConfigurationManager.AppSettings["SomeSettingThatIsUniquetoSite"]);
// Modifying underlying baking field that points to FormsAuthentication.FormsCookieName
Type type = typeof(FormsAuthentication);
System.Reflection.FieldInfo field = type.GetField("_FormsName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
field.SetValue(null, newCookieName);
}
Suggestions, loopholes are requested as this is my first answer on this forum.
You can't do this in code; the property FormsAuthentication.FormsCookieName is readonly. I would use the following configuration in web.config:
<authentication mode="Forms">
<forms configSource="forms.config" />
</authentication>
Then give each instance of the application its own forms.config:
<forms name="CookieNameForThisInstance" />
I have ASP.NET MVC 4 application. I use Simple Membership Provider allowing to tick remember me checkbox under login form. If ticked, persitent cookie .ASPXAUTH is created which expires 100 days from login date. And everything works fine, apart of main menu of the application.
Some parts of the menu are available for admin users only. I use:
#if (User.IsInRole("Administrator")) { #Html.Partial("_MainMenuPartial") }
to lock them from being rendered for regular user. This approach works fine just after logging into the system. When I return after a while and persistent cookie is used to authenticate me, I do get logged in, but
#User.IsInRole("Administrator")
returns "False" so I can't see admin menu items. At the same time
#User.Identity.Name
returns proper login name and
#User.Identity.IsAuthenticated
returns "True", what proves that persistent cookie works fine. Why can't I access user roles even though user is authenticated by the system then?
I had a smilar issue. In my case the problem solved when i log off and log in again.
Problem using roles with MVC5
I found a solution. In my web.config:
<modules>
<remove name="FormsAuthenticationModule" />
<remove name="RoleManager" />
</modules>
I added remove name="RoleManager" line, and the new AspNet.Identity code took over allowing me to use User.IsInRole(..)
http://forums.asp.net/t/1952572.aspx?Problem+using+roles+with+MVC5
Old question, but there's some missing information here. I ran into the same issue, and there's one thing to keep in mind. If you test the user identity (like User.IsInRole("admin")) just after authenticate (without send the response to client), Identity framework has not populated this session info yet (or at least, I could not found the way to do that). This info you're requesting for, is populated at some point after your controller return. If you want (I.E.) to redirect to different views after login, depending on the user role, you'll have to use a custom authorization filter.
Have you tried adding
[InitializeSimpleMembership]
in the controller where you're doing the check?
I had a similar problem with IsUserInRole returning false. By placing a watch on it, I was able to overcome the problem by using the overload described below. Try it, put a break point where you're getting false, and see what happens.
#if (Roles.IsUserInRole(Model.UserName, "Administrator"))
I'm pretty sure you could also use User.Identity.Name as the first parameter.
If you are using latest Identity framework please see if you are using following
services.AddIdentity<IdentityUser,IdentityRole>()
.AddEntityFrameworkStores<WebUserContext>()
.AddDefaultTokenProviders();
If you are using AddDefaultIdentity Roles don't get populated.
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<WebUserContext>()
.AddDefaultTokenProviders();
I had a similar issue, seems like authentication module is not wired up completely with the SimpleMembershipProvider. To get around that problem, you can use a Role class to access needed information. For example to check if the use in role you can use the following:
Roles.GetRolesForUser("sergey").Contains("Developer")
And so other useful methods when working wit SimpleMembershipProvider roles:
Roles.RoleExists("Developer")
Roles.CreateRole("Developer");
Roles.AddUserToRole("sergey", "Developer");
The "EmailConfirmed" of this account probably still false, try to change it to true
None of these answers worked for me, but I did find the answer here: https://thinkthencode.wordpress.com/2016/04/24/azure-ad-using-app-roles-for-authorization/
The key was adding the following to Startup.Auth.cs:
TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "roles"
}
If you're using IdentityManager by Brock Allen to create roles and assign them to users then you should read this article: https://github.com/IdentityManager/IdentityManager.AspNetIdentity/issues/3
You'll need to uncomment the following line:
this.RoleClaimType = System.Security.Claims.ClaimTypes.Role;
Now you'll realize that your user that used to be in roles is no longer in them, and you'll have to re add them. If you take a look at the AspNetUserClaims table, you'll see claimType of Role and 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role' you want the later.
Once you do this, User.IsInRole("rolename") behaves as expected.
I hope this helps someone, took me a while to figure this out.
I had a similar issue, but in my case the problem was that the Session timeout was not synced with the authentication timeout, so I was not kicked out automatically but my session was being expired and, since my allowed operations were stored in a Session Variable, I was not able to retrieve the correct options for the current user.
Try checking if your session is not expired even if you're still logged in
Quite old topic but can confirm in vs 2015 Web forms application works solution:
<modules>
<remove name="FormsAuthenticationModule" />
<remove name="RoleManager" /> <!--add this to web config-->
</modules>
I've been struggling with the MVC5 role manager for some time now. I've verified that User.IsInRole can return different results at the same time, and apparently this is because the User object is cached and needs to be invalidated. One way to do this is to log out and log back in again as one of the answers here specified. Just throw this in the controller that adds the new role:
UserManager.AddToRole(User.Identity.GetUserId(), "Administrator");
var updatedUser = await UserManager.FindByNameAsync(User.Identity.Name);
var newIdentity = await updatedUser.GenerateUserIdentityAsync(UserManager);
AuthenticationManager.SignOut();
AuthenticationManager.SignIn(newIdentity);
However, if the user has your application open in other windows this won't update all of them.
Clear all cookies and try to login again. If we changed our roles, the cookie might not be aware of it. Reset the Cookies and create a cookie with your new roles and try again.
Paste this code in Global.asax
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket =
FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = authTicket.UserData.Split(new Char[] { ',' });
GenericPrincipal userPrincipal =
new GenericPrincipal(new GenericIdentity(authTicket.Name), roles);
Context.User = userPrincipal;
}
}
protected class RolesAttribute : AuthorizeAttribute
{
public RolesAttribute(params string[] roles)
{
Roles = String.Join(",", roles);
}
}
Several things could be going on and here are few general things to try:
Try to run in debug mode and make sure that the line of code gets executed - silly but might happen
As stated in other answers, try to log off and log back in
Make sure the role is spelled out correctly
Make sure that the proper user is assigned to the proper role id
Try to republish the entire solution to the server - in my case, everything worked fine locally but not on the server until full delete all and republish
If all fails, try to run out of a box project and test it before adding complexity
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
...
await UserManager.IsInRoleAsync(await UserManager.GetUserAsync(User),"admin")
Add Page in front of User. This works: Page.User.IsInRole("Administrator")
For my code it had to be plural, not singular:
User.IsInRole("Administrators")
Really struggling with an MVC / TempData / Session / possibly IIS6 type issue.
I've got a simple MVC website - primary controller looks like this:
public class DemoController : Controller
{
[HttpPost]
public RedirectToRouteResult Index(SomeObject obj)
{
_someService.DoStuffWith(obj);
TempData.Add("SomeObject", obj);
return RedirectToAction("Index");
}
[HttpGet]
public ViewResult Index()
{
var obj = TempData.Peek("SomeObject") as SomeObject;
return View("Hello", obj);
}
}
So this is a mega-simple Post Redirect Get - submit data from somewhere, get it in the Post-friendly action, do stuff with it, poke it into temp data, redirect to Get-friendly, pick it up again, stick the user on a new view with the object in the model. Have implemented this a dozen times before, and never had a problem, but it's always been on IIS7.
This works exactly as expected when running locally, both on cassini, and on local IIS 7.5. However, as soon as I deploy to Server 2003 and IIS6, the first time I try to access any property of the "SomeObject" model in the view, I get a null reference exception.
So, what else have I tried:
I've ditched TempData and just pushed it into Session["Whatever"] - with same results
I've pushed into HttpContext.Current.Session["Whatever"] - with same results
I've pushed into ControllerContext.HttpContext.Session["Whatever"] - with same results
(Admittedly, i'm sketchy on the differences between these - have never needed to worry about it before now)
I've made sure session state is switched on on IIS.
I've checked that the Asp.net session identifier is not changing between requests.
I've deployed to 2 separate servers, both running 2003 and IIS6, same results on both
Explicitely used SessionStateBehaviour.Required
What i'm not able to do:
Deploy to IIS7, outside my local machine
Use SqlServer SessionState
Use StateServer SessionState
Also worth mentioning that this site is using the MVC4 RC - though the RC features are not used in this particular section of the site.
Any ideas or comments are most welcome!
Thanks.
This was the problem on my system - I had a server name that contained underscores. Underscores are not allowed in host names by RFC 952 and may interfere with the ability to set cookies and thus to persist sessions.
To track this down I started watching the SessionID in each controller section and noticed that with every post it was changing. This was only happening on our test server because we named it with _t to show it was a test machine. OUCH!
I found this point.
That may offer other value.
I hope this helps
David
The problem for me was I had this set in my web.config file, but wasn't using SSL:
<httpCookies requireSSL="true" />
This setting prevents cookies from working over unsecure (i.e. non-SSL) connections - including the ASP.NET session cookie.
I am running into an issue with ASP.NET MVC where it is forcing the user to log back in after about 20 mins of inactivity.
I am using Forms Authentication and have increased the time-out in the config file as:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="9999999" />
</authentication>
I am also setting the session time-out in the config file as:
<sessionState timeout="120"></sessionState>
I am basing this off of Rockford Lhotka's CSLA ASP.NET MVC example and have the following in my global.asax:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Handler is IRequiresSessionState)
{
if (Csla.ApplicationContext.AuthenticationType == "Windows")
return;
System.Security.Principal.IPrincipal principal;
try
{
principal = (System.Security.Principal.IPrincipal)
HttpContext.Current.Session[MyMembershipProvider.SESSION_KEY];
}
catch
{
principal = null;
}
if (principal == null)
{
if (this.User.Identity.IsAuthenticated && this.User.Identity is FormsIdentity)
{
// no principal in session, but ASP.NET token
// still valid - so sign out ASP.NET
FormsAuthentication.SignOut();
this.Response.Redirect(this.Request.Url.PathAndQuery);
}
// didn't get a principal from Session, so
// set it to an unauthenticted PTPrincipal
BusinessPrincipal.Logout();
}
else
{
// use the principal from Session
Csla.ApplicationContext.User = principal;
}
}
}
From what I can tell it should ONLY time-out after 120 minutes of inactivity ... but for some reason it always seems to time-out after 20 minutes of inactivity. I have know idea why this is happening, any ideas?
I am toying with the idea of just dumping Forms Authentication and handling it myself via Session, but I'm afraid I would lose functionality like [Authorize] attributes and so on. Trying not to go down this path.
Is it possible to store my custom principal object as a cookie? I just don't want to have to authenticate/authorize a user for every single page or action.
I'm losing hair ... rapidly! =)
Mixing concerns of FormsAuthentication with SessionState is just a bad idea on many levels, as you are noticing from the answers you are getting.
If the information describing your custom principal is small, I would suggest storing it in the UserData member of the forms ticket. That is what it is there for.
Then your custom data, which is only valid with a valid ticket, is stored with the ticket.
Many problems solved and mucho code obviated.
Here is a helper class that can help you with your ticket.
CAVEAT: In practice the max http cookie size is just shy of the official 4k limit and Encryption cuts that in half approximately.
If you can ensure that your ticket, including principal data will fit into <2k you should be good to go. Creating a custom serialization for your principal can help, e.g. name=value pairs works great if your data will cooperate.
Good luck.
Handling it via Session may not be enough. Because it could be IIS recycling your application, therefor causing all the sessions to be abandoned.
See
[recycling] [iis]
[recycle] [iis]
Hopefully you got this solved by now, but in case somebody else comes along with the same issues, I've been responsible for debugging some code written using the same template, and here are a few thoughts:
1) The forms ticket has a timeout encoded into its value. Most of the example code out there hard-codes this timeout instead of pulling from the forms auth configuration, so if you're just looking at your web.config everything can look fine but your custom security code is ignoring the web.config value. Look through your code for "new FormsAuthenticationTicket" and see what you are doing for the expiration time.
2) The forms cookie has a timeout set in its cookie value. Some of the example code out there hard-codes this timeout. Look and see if you are setting cookie.Expires on your security cookie. (Custom auth tends to hand-build more code here than you would expect because the FormsAuthentication methods don't expose the make-a-cookie-with-userdata method, and you generally want to use userdata to store a bit of info like roles in)
3) Some clients will not set a cookie on response redirect. And sometimes even if they do, you'll get back a cookie other than the one you set. For example, if you have changed the app path or domain at any point, it's possible for the user to have two valid cookies, and you're only clearing one when you try to log them back in here. Now, this code basically reads "The user has some session info, and was logged in, but their session didn't contain the principal I expected it to, so I redirect them to login again." Well, if they don't listen to your auth cookie, or have an auth cookie you don't expect (maybe you changed your domain or path values at some point and they have a /oldpath cookie still set), this can infinite loop. I recommend nuking the session server-side as soon as you find out that it doesn't have the data you want: Session.Clear() - this leaves you less likely to end up in this situation after a redirect. (From a recover-server-side-without-trusting-the-client-to-behave perspective, it's actually slightly safer to go ahead and reconstruct the principal object and put it into the session, but I can see how this would be less secure.)
It's also safer to just do a Server.Transfer to the login page rather than relying on a cookie-changing redirect to work right. If you do end up in a redirect loop, server.transfer is guaranteed to end it.
Are you using the membership provider for authorization also? If so you may want to look at the userIsOnlineTimeWindow attribute. The default for this is also 20 minutes.
I have a problem with my Authentication HttpModule. The problem is that it obviously runs for every single request I get on my web server (IIS7). Because it also uses Session variable, it fails to work properly on CSS, JS files and similar.
I tried to use:
<add name="AuthModuleName" type="..." preCondition="managedHandler" />
but to no avail. It still runs on every request regardless of its extension or mime type. I should also add, there's a setting
<modules runAllManagedModulesForAllRequests="true">
that seemed suspicious to me and actually disabled preConditions on modules. But changing it to false, breaks the application in a completely different way and with a different exception (The SessionStateTempDataProvider requires SessionState to be enabled).
Can anybody help me how to force IIS7 to exclude my HttpModule when requests are made for static content files?
runAllManagedModulesForAllRequests attribute has to be set to false to actually configure any module the way you want. You will have to also correctly reconfigure Session and others as needed, but the main thing is handlers pipeline execution order that handles requests.
The answer was provided in one of my other questions:
Thanks to Peter that provided the answer that worked correctly.
I don't know about an IIS7 setting for that but you can do this.
The session object will be available only for non-static content :
void yourEventHandler(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
if (app.Context.Session == null) {
return;
}
// then your code here...
}
This will ensure your code won't be run for files like CSS, JS etc. But keep in mind the session object will also not be ready before PostAcquireRequestState event. (For the order of the HttpApplication events, see this page.)
Edit : Also, it appears with ASP.NET Development Server (though I know you said IIS7 in your question), your HttpModule will still run even for static files.