ASP.NET MVC Session.IsNewSession issue with Google Chrome - c#

I'm currently working on a Session expired piece of logic for my ASP.NET 3.5 MVC 2 project to log out a user and redirect them to the AccountController LogOn action.
I have the following attribute on all my actions that care about session state, and this piece of code works in IE 8, but not Firefox 4 or Google Chrome 10. The symptom is when I attempt to navigate to a view represented by an action with my [SessionExpireFilter] attribute, the ctx.Session.IsNewSession property in the below code is evaluating as "true" every time, even if I'm only seconds into my 30-minute session.
public class SessionExpireFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check if session is supported
if (ctx.Session != null && ctx.Session.IsNewSession)
{
// If it says it is a new session, but an existing cookie exists, then it must
// have timed out
string sessionCookie = ctx.Request.Headers["Cookie"];
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0))
{
FormsAuthentication.SignOut();
ctx.Response.Redirect("~/Account/LogOn");
}
}
base.OnActionExecuting(filterContext);
}
}
Is there any way to figure out why Chrome and Firefox are behaving this way, but IE is not? Thanks in advance.
EDIT: This is not working in FF as I originally believed. I am being directed to my LogOn action immediately after logging in and attempting to access an action with my SessionExpireFilter attribute.

ASP.NET will create a new session for every request unless you store something in it.
Try adding the code below to your Global.asax. It works in my MVC2 and MVC3 apps with the same SessionExpireFilterAttribute.
protected void Session_Start()
{
Session["Dummy"] = 1;
}

We can add session_start method in Golbal.asax file in MVC appliaction.
protected void Session_Start(object sender, EventArgs e)
{
HttpContext.Current.Session.Add("UserId" , null);
}
Then when application starting your session will be created. and then session will be not isNewSession 'True', otherwise it will be always 'True'

Related

OWIN Produces Different Behavior Between IIS Express and IIS

I'm building a authentication app using OWIN. I'm trying to get both the Bear token and userinfo claims. The code below gets me to 85% of what I want. While initially writing the code I used IIS Express. I debugged and coded towards that environment. For whatever reason after the initial challenge called in the else block the request.isauthenticated is false after the return from the login screen (Using KeyCloak as idp). The code then drops the user into the else if block where I find request.form has my Bearer token. I must then execute the authentication.challenge again (no KeyCloak login screen opens) and I return to the top of the page_load and this time the request.isauthenticated is true and I can get the userinfo but the request.form is empty. This is find for me because I can store all the info off somewhere for later use.
Once I got to this point I targeted IIS. Ran the code and got different behavior. The code drops into the else block initially (same as before) I login but upon return from the idp this time the request.isAuthenticated is true. I have the userinfo but not the Bearer token. Any ideas why??
protected void Page_Load(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
String str = String.Empty;
var qry = ((System.Security.Claims.ClaimsPrincipal)Request.RequestContext.HttpContext.User).Claims;
if (null != qry)
{
foreach (System.Security.Claims.Claim item in qry)
{
if (item.Type == "preferred_username")
{
str = item.Value;
}
}
}
}else if (!Request.IsAuthenticated && Request.Form.Count > 0)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
else
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/XXXapp locationXXX/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
I've figured it out,. Needed to set the save token flag to true. This allowed the token to be carried along in the request. So, I don't need if else. Now that I got that working I'm changing this section of code. My main issue is it is hard to find complete and current documentation with sample code for my use case. --Thanks

OWIN Challenge method does nothing

I'm trying to integrate a Microsoft account login into my ASP.NET MVC app, and I have this controller method:
public void SignIn()
{
// HACK - we will be signed into only one account if we are not signed in to MS
if (Request.GetOwinContext().Authentication.User.Identities.Count() <= 1)
{
// Signal OWIN to send an authorization request to Azure
Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "http://localhost:31503/MicrosoftCalendar" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
What I expect to happen is that I'm prompted to log in to my Microsoft account; instead, what happens is this method runs over and over and over again, doing nothing at all, until I get a "too many redirects" error in my browser. How can I get the Challenge method to actually do something?
I have a class OwinStartup in my web project; I have it set to be the OWIN startup class like so:
[assembly: OwinStartup(typeof(Root.OwinStartup))]
However for some reason my breakpoints inside this startup class never get hit; OWIN is never being initialized... actually, wait a second, it is being initialized, but the event handlers for things like OnAuthorizationCodeReceivedAsync are never being hit...
If I step through the code, after Challenge is called in the SignIn method, I get redirected for some reason to a UserController, which in turn redirects me back to the SignIn method. I wonder why I'm winding up in the UserController?
edit: I need more code? all right, this method in Global.asax.cs executes immediately after the OWIN calls:
protected void MvcApplication_BeginRequest(object sender, EventArgs e)
{
#region Set the context GUID cookie
if (null == Request.Cookies[CookieName.ContextGUID])
{
Response.SetCookie(new System.Web.HttpCookie(CookieName.ContextGUID, Guid.NewGuid().ToString()));
}
#endregion
// check to see whether SSL is required
if (System.Web.Security.FormsAuthentication.RequireSSL)
{
// check where the request is originating from
if (Request.UserHostName != "127.0.0.1" && Request.UserHostName != "localhost")
{
// check if the request is secure
if (!Request.IsSecureConnection)
{
string url = null;
// check for querystring segments
if (!String.IsNullOrEmpty(Request.ServerVariables["QUERY_STRING"]))
{
url = String.Format("https://{0}{1}?{2}",
Request.ServerVariables["SERVER_NAME"],
Request.ServerVariables["SCRIPT_NAME"],
Request.ServerVariables["QUERY_STRING"]);
}
else
{
url = String.Format("https://{0}{1}", Request.ServerVariables["SERVER_NAME"], Request.ServerVariables["SCRIPT_NAME"]);
}
// redirect to the secure url
Response.Redirect(url);
}
}
}
// verify the request
if (null != Request)
{
// NOTE: This is a workaround for the following exception thrown by the ReportViewer control when
// using a non-IE browser:
// Missing URL parameter: IterationId
// See the following reference: https://connect.microsoft.com/VisualStudio/feedback/details/556989/?wa=wsignin1.0
if (Request.Path.EndsWith("Reserved.ReportViewerWebControl.axd") &&
Request.QueryString["ResourceStreamID"] != null &&
Request.QueryString["ResourceStreamID"].ToLower().Contains("blank.gif"))
{
// intercept the request and send to actual valid image path
Response.Redirect(Constant.ImageRoot + "blank.gif");
}
}
}
Not sure if this is what's causing the infinite redirect loop but here it is...
This is maybe a shot in the dark, but it looks like the controller isn't returning anything because it is a void method, try adding a return type, I'm not overly familier with OWIN so you'll have to forgive me there but here is an example of what I'm talking about:
public ActionResult SignIn()
{
// Signal OWIN to send an authorization request to Azure
return Request.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "http://localhost:31503/MicrosoftCalendar" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
two small changes which are in the method signature, returning ActionResult and not a void, you may have to do a bit of research here on the class that OWIN actually returns. and second adding the return keyword, note this will not work with the if statement that you have said is a "hack" because you would require two return statements in that scenario
Hope this helps.

Http module Authentication

I'm using the code below from this post to try and create a custom http module :
public class BasicAuthenticationModule: IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new EventHandler(Do_Authentication);
}
private void Do_Authentication(object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
string header = request.Headers["HTTP_AUTHORIZATION"];
if(header != null && header.StartsWith("Basic "))
{
// Header is good, let's check username and password
string username = DecodeFromHeader(header, "username");
string password = DecodeFromHeader(header, password);
if(Validate(username, password)
{
// Create a custom IPrincipal object to carry the user's identity
HttpContext.Current.User = new BasicPrincipal(username);
}
else
{
Protect();
}
}
else
{
Protect();
}
}
private void Protect()
{
response.StatusCode = 401;
response.Headers.Add("WWW-Authenticate", "Basic realm=\"Test\"");
response.Write("You must authenticate");
response.End();
}
private void DecodeFromHeader()
{
// Figure this out based on spec
// It's basically base 64 decode and split on the :
throw new NotImplementedException();
}
private bool Validate(string username, string password)
{
return (username == "foo" && pasword == "bar");
}
public void Dispose() {}
public class BasicPrincipal : IPrincipal
{
// Implement simple class to hold the user's identity
}
}
The code works ok at making the 401 error be returned by the server and the login dialog pop up but when the correct login details are entered the login dialog does not go away.
When debugging the code nothing happens when the Ok button on the dialog is clicked, the event isn't triggered and the user details aren't validated, I can't figure out why this isn't working.
Any help or ideas would be great, thanks.
On Microsoft's asp.net website, there is a good example on how to do custom authentication. Ignore the fact that it says it's about WebAPI. The code uses a IHttpModule, so it works with WebForms, IHttpHandler, asmx, WCF, and anything else that runs in IIS. Copying and pasting the code at the end of that page into a new project works for me. Although, I don't recommend setting the thread's CurrentPrincipal to the authenticated user, like the sample does. I prefer to just use the current context's User property.
If your breakpoint in the module isn't getting hit, then it's almost certainly because the http module wasn't registered correctly. The asp.net page I linked above shows how to register the module in your web.config file, so you should start there. You should be able to use Visual Studio's intellisense auto-complete to complete your class name, which helps make sure you typed it right (although there is a chance that Resharper is doing it on my machine, but I think it's just plain Visual Studio).

Azure cloudapp.net domain and duplicate content issue

I have a C#/MVC4 site hosted on Azure as a web role located at http://www.equispot.com. During a check on Google for some searches related to my site, I came across a search result that links to this page:
http://equispot.cloudapp.net/horses-for-sale/quarter-horses/13
Note the difference in the domain name. Now, I have a canonical tag already (view the source on the cloudapp.net link and you can see the canonical rel tag points to the main site at http://www.equispot.com).
Since that's the case, why would Google have indexed the page at the cloudapp.net domain? I recently noticed a drop in my SERPs and I'm wondering if this is part of the reason (I migrated to Azure about the same time as the SERP change). It may be unrelated but still...
How can I prevent these pages from being indexed by Google or how can I prevent my Azure web role from responding to anything except www.equispot.com and equispot.com? When I had this hosted on premise, I just configured IIS to respond only to my domain (my previous provider produced some dupe content for some reason as well).
You can simply check to make sure that the host the application is running under is the domain name you want. If it is not, then simply do a 302 redirect to the domain name you want.
There are several places where you can inspect the request and do the redirect:
- Global.asax
- Custom module
- Override the OnActionExecuting for action methods
I couldn't find a straightforward way to do this using hostHeader configuration in the ServiceDefinition.csdef file so I rolled my own RedirectInvalidDomainsAttribute class to perform a 301 (Moved Permanently) redirect back to my main site during a request for an invalid domain. In case anyone else runs into the same problem, here's the code:
App_Start/FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RedirectInvalidDomainsAttribute());
}
RedirectInvalidDomainsAttribute.cs
public class RedirectInvalidDomainsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var url = filterContext.HttpContext.Request.Url;
if (url == null) return;
var host = url.Host;
if (host.Contains("equispot.com") || host.Contains("localhost")) return;
string subdomain = GetSubDomain(host);
Guid guid;
if (Guid.TryParseExact(subdomain, "N", out guid))
{
// this is a staging domain, it's okay
return;
}
// Invalid domain - 301 redirect
UriBuilder builder = new UriBuilder(url) {Host = "www.equispot.com"};
filterContext.Result = new RedirectResult(builder.Uri.ToString(), true);
}
// This isn't perfect, but it works for the sub-domains Azure provides
private static string GetSubDomain(string host)
{
if (host.Split('.').Length > 1)
{
int index = host.IndexOf(".");
return host.Substring(0, index);
}
return null;
}
}

HTTP Module and Cookies in Sharepoint 2007

I have some proof concept code for a HTTP module. The code checks to see if a cookie exists, if so it retrieves a value, if the cookie does not exist it creates it and sets the value.
Once this is done I write to the screen to see what action has been taken (all nice and simple). So on the first request the cookie is created; subsequent requests retrieve the value from the cookie.
When I test this in a normal asp.net web site everything works correctly – yay! However as soon as I transfer it to SharePoint something weird happens, the cookie is never saved - that is the code always branches into creating the cookie and never takes the branch to retrieve the value - regardless of page refreshes or secondary requests.
Heres the code...
public class SwithcMasterPage : IHttpModule
{
public void Dispose()
{
throw new NotImplementedException();
}
public void Init(HttpApplication context)
{
// register handler
context.PreRequestHandlerExecute += new EventHandler(PreRequestHandlerExecute);
}
void PreRequestHandlerExecute(object sender, EventArgs e)
{
string outputText = string.Empty;
HttpCookie cookie = null;
string cookieName = "MPSetting";
cookie = HttpContext.Current.Request.Cookies[cookieName];
if (cookie == null)
{
// cookie doesn't exist, create
HttpCookie ck = new HttpCookie(cookieName);
ck.Value = GetCorrectMasterPage();
ck.Expires = DateTime.Now.AddMinutes(5);
HttpContext.Current.Response.Cookies.Add(ck);
outputText = "storing master page setting in cookie.";
}
else
{
// get the master page from cookie
outputText = "retrieving master page setting from cookie.";
}
HttpContext.Current.Response.Write(outputText + "<br/>");
}
private string GetCorrectMasterPage()
{
// logic goes here to get the correct master page
return "/_catalogs/masterpage/BlackBand.master";
}
This turned out to be the authentication of the web app. To work correctly you must use a FQDM that has been configured for Forms Authentication.
You can use Fiddler or FireBug (on FireFox) to inspect response to see if your cookie is being sent. If not then perhaps you can try your logic in PostRequestHandlerExecute. This is assuming that Sharepoint or some other piece of code is tinkering with response cookies. This way, you can be the last one adding the cookie.

Categories

Resources