Forms Authentication Not Validating User properly - asp.net

I have this code when to sign in User , that string sUserData is properly set.
Dim sUserData As String = HttpContext.Current.Request.Cookies("UserID").Value & "|" & HttpContext.Current.Request.Cookies("UserName").Value & "|" & HttpContext.Current.Request.Cookies("UserEmail").Value
Dim fat As FormsAuthenticationTicket = New FormsAuthenticationTicket(1, _
HttpContext.Current.Session("UserID"), DateTime.Now, _
DateTime.Now.AddDays(6), True, sUserData, _
FormsAuthentication.FormsCookiePath)
HttpContext.Current.Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)))
Then I have code where I check if the user if signed in in a Shared (static) method in a Public Class like this :
If HttpContext.Current.User.Identity.IsAuthenticated Then
EndIf
And that works just fine , but if I put the same line in Page_load instead of a Shared Method of a class it will never go into this If statement
If HttpContext.Current.User.Identity.IsAuthenticated Then
EndIf
Why is this happening , and is there some way to re-write this to work in the code-behind Page_Load instead of having to put it in a class ,The class is used in a header to allow access to certain pages - so that works fine. But I need another way of authentication of user on Default page to change labels and buttons based on weather the user is logged in or not , and this can not be done in a class.

Have you tried putting the page event overrides into an actual page event override (i.e. OnLoad) instead of the Page_Load event hook implementation? More performant (fewer layers of invoke), slight difference in life-cycle which may suit your needs and may distill the cause of these symptoms.
There may be a sequencing issue / race condition if the context of the static method call and the Page_Load, I think Wiktor Zychla pointed you in the direction of fiddler already.

Related

asp.net vb2010 How to log Windows Authentication events - i.e. save user name and login time

Just now getting into the asp.net environment with VB 2010. I have designed an intranet page that we will be using to auction off old equipment to employees. I've had some very good luck getting the whole thing working, but I have hit a snag on one minor issue: I would like to keep a database table of user logins and times.
Several methods have presented themselves, but so far none of them have been adequate. The main problem seems to be that regardless of the event I use to record the login, they fire multiple times resulting in as many as 4 or 5 entries in the login database for the same login event, not to mention additional times when something on the page changes for whatever reason.
The system uses Windows Authentication, so the user must login with his/her company credentials.
I can't remember what all I have tried, but the first thing I tried was the Page_Load event because it seemed obvious. I've learned that because the page updates the display every ten seconds, Page_Load event fires numerous times throughout the session.
Fighting my way through other ideas, all of which failed, I came to the WindowsAuthentication_OnAuthenticate event in the Global_asax class, shown below:
Imports System.Web.SessionState
Imports System.Data.SqlClient
Imports System.Web
Imports System.Web.UI.WebControls
--------------------------------------------
Public Sub WindowsAuthentication_OnAuthenticate(ByVal sender As Object, ByVal args As WindowsAuthenticationEventArgs)
Dim strLoginName = args.Identity.Name
Dim sqlCommandString As String = "INSERT into tblLogin (UserLogin,DateTime) VALUES ('" & _
strLoginName & "','" & _
Format(Now, "MMM dd, yyyy hh:mm:ss") & "');"
Dim sqlConn As New SqlConnection(strConnectionString)
Dim sqlCommand As SqlCommand = New SqlCommand(sqlCommandString, sqlConn)
sqlCommand.Connection.Open()
sqlCommand.ExecuteNonQuery()
sqlCommand.Connection.Close()
End Sub
which also seems to fire multiple times. I then tried to work a way where setting a Boolean value to TRUE upon authentication so that the data would be written to the database only once:
Public Sub WindowsAuthentication_OnAuthenticate(ByVal sender As Object, ByVal args As WindowsAuthenticationEventArgs)
If Not boolUserIsLoggedIn Then
boolUserIsLoggedIn = True
Dim strLoginName = args.Identity.Name
Dim sqlCommandString As String = "INSERT into tblLogin (UserLogin,DateTime) VALUES ('" & _
strLoginName & "','" & _
Format(Now, "MMM dd, yyyy hh:mm:ss") & "');"
Dim sqlConn As New SqlConnection(strConnectionString)
Dim sqlCommand As SqlCommand = New SqlCommand(sqlCommandString, sqlConn)
sqlCommand.Connection.Open()
sqlCommand.ExecuteNonQuery()
sqlCommand.Connection.Close()
End If
End Sub
and subsequent authentication iterations would skip the database write code; but then I needed to find a way to clear the Boolean value when the user logged off or closed the session. I couldn't figure out how to trap a "Logged Off" event, and the Session_End and Application_End events didn't work as I had hoped.
I feel like this is getting too convoluted to make sense. To put it simply, I just need a way to record a user's login name and date only once per session. Can anyone help?
[Edit: Although this is not really an essential element of the page, it has become a crusade for me to get it to work if not for any other reason than to understand the process.]
Researching more about JDB's suggestion using the session ID property, I discovered another property that seems to be doing exactly what I need: The IsNewSession property.
With that in mind, I have this code in my default.aspx's Page_Load event:
If Context.Session.IsNewSession Then
Login()
End If
Which calls the Login sub that writes the login information to the database:
Protected Sub Login()
Dim strConnectionString As String = "Data Source = toponet\sqlexpress; initial catalog = TopoAuction; Integrated security = True"
Dim strLoginName = System.Web.HttpContext.Current.User.Identity.Name
Dim sqlCommandString As String = "INSERT into tblLogin (UserLogin,DateTime) VALUES ('" & _
strLoginName & "','" & _
Format(Now, "MMM dd, yyyy hh:mm:ss") & "');"
Dim sqlConn As New SqlConnection(strConnectionString)
Dim sqlCommand As SqlCommand = New SqlCommand(sqlCommandString, sqlConn)
sqlCommand.Connection.Open()
sqlCommand.ExecuteNonQuery()
sqlCommand.Connection.Close()
End Sub
So far, testing has shown that it fires only once per session. Even if the browser moves to another site and comes back, it will not count that as a new session. This seems to be the perfect solution.
I'm open to hear any comments or concerns about this, anybody?
I think, perhaps, you are getting hung up on the request/response cycle of a web page. If you have only ever used WebForms and come from a desktop development background, the statelessness of the HTTP protocol can be very confusing.
Essentially, the client (the browser) requests information from the server (where your .NET code is running). Each request is completely independent from every other request - the server maintains no memory of prior requests.
Now, WebForms was an attempt to bridge the gap by shoe-horning on a sort of pseduo-statefullness on top of HTTP. This is where ViewState and other similar concepts come in. Basically, WebForms includes additional information about the state of the web application in each Response, which is then included again in following Requests, maintaining the application's state and giving the appearance of statefulness. There are other tricks too, like server variables, which can be used to maintain some "memory" of past transactions.
However, this statefulness is easily broken. For example, if you include a basic anchor tag (not an ASP.NET server control), and you redirect to another page (or even the same page) without including the view state, then you will lose some of your application state. Even if you are using server controls and maintaining your view state, you must pay careful attention to the WebForms Page lifecycle. If you are handling the wrong event (such as Page_Load) or attempting to access a property at the wrong time (before the web page has had a chance to reload it's state), you may not get the results you are expecting.
In this scenario, I recommend you check the user's Session ID. It changes very infrequently. If you have a user ID coming in with a session ID you've not seen before (or that's not been associated with that user in some amount of time - say 30 minutes), then you probably have a unique logon event. If you have seen that user ID/Session ID combo, then that user has probably already "logged in" and you don't need to record it again.
Alternatively, just store the user's login state in a session variable. You can check that variable on each Page_Load and branch (using an If statement) based on your needs.

Session becoming Null after Response.redirect

I set two sessions which I fill from a database:
Session("username") = reader.Item("user_name").ToString
Session("department") = reader.Item("user_department").ToString
to add restrictions depending on department the user is signing in from (IT department, customer service, etc..)
Sessions are readable from the form LogIn.aspx to the form Default.aspx
But in other pages:
IF Session("Department")<>"IT"
Response.Redirect("LogIn.aspx")
End If
This redirects to LogIn.aspx and Session("Department") equals Nothing
Any idea on why it is doing so? I tried searching for something missing in my code and I couldn't find anything.
The key is case sensitive, so change it to "department". Currently it's set to "Department"
Instead of this
IF Session("Department")<>"IT"
use this
IF Session("department")<>"IT"
spell mistake( Keys are case sensitive).
And use if condition with safety like this
If Session("department") IsNot Nothing AndAlso Not Session("department").ToString().Equals("IT") Then

Show Logged In User Name in redirect.aspx Page

I'm a complete stranger to ASP.NET. But, I've had a project to do using it & faced a problem.
It is :
I have a login.aspx File - Where Users provide login User name & Password
If Login details (match Data Base) OK then User automatically redirects to logged_in.aspx.
There's a label (lbl_show) in redirected logged_in.aspx.
I need to show Logged in Username in it.
I read bunch of articles & came with nothing because of my lack of understanding so please help me.
se session variables in order to pass any value from one page to another.
Assign the Username value to the session variable and use it in your logged_in page as follows:
// In login page
Session["UserName"] = txtUserName.text;
//In logged_in page
label1.text = Session["UserName"];
Also refer the following link for State Management:
http://www.codeproject.com/Articles/492397/State-Management-in-ASP-NET-Introduction
You need to set an Authentication Cookie. It's easy and will allow you to leverage ASP.NET functionality easily (many built-in controls and also user-access control). I detail how in this SO post:
Using cookies to auto-login a user in asp.net (custom login)
The problem with the code
// In login page
Session["UserName"] = txtUserName.text;
//In logged_in page
label1.text = Session["UserName"];
Is casting is missing it should be
label1.text = Session["UserName"].ToString();
Edit 1
As Session contains object and if you have something other than object then you will have to explicitly cast it in your require type.
Suppose you have array in you Session then you will have to cast it back to array.
String[] Names={"abc","def","ghi"};
Session["NamesCol"]=Names;
Then if you want to use it you will have to cast it as follow
String[] NewNames=(string[])Session["NamesCol"];

ASP.NET ScriptManager History url hash lost after redirect

I have seen several posts online complaining that Firefox maintains the history url hash after redirecting.. That is the behavior I am hoping for - and it happens in Firefox (11.0), Chrome (18.0), and Opera (11.61), but not IE (9) or Safari (5.1.2).
On my page, I have ASP.NET 4.0 history points set up and working (has been working for a couple years). I also pass a few querystring params to the page. What I am trying to do now is check the value of a new querystring param, and if it does not match what I am expecting, redirect to the same page with an updated value. I am using this mechanism to track the session of individual tabs of a browser so that when they have the same page open in multiple tabs, the session values dont step on each other from tab to tab.
Anyway, I have everything working correctly including the back/forward using ASP.NET History points - and when I visit a bookmark and the querystring param does not match, I redirect and change the querystring param to track the session, and the history state that is in the url of the bookmark is then used to reload the page to the state I want. But that only works in Firefox, Chrome, and Opera. Not IE which is the big one for me (based solely on our user base), and not Safari.
I have identified that in addition to (or perhaps because of) the fact that the url history state is not present, ScriptManager.Navigate is not called after the redirect in IE or Safari.
Is there a setting/option that I can set on the ScriptManager or during the redirect to maintain the History state in the url? If the history state was in the url, I could call ScriptManager.Navigate directly if I needed to, but the values are not present in the url.
If it helps at all, here's a listing of where I do the check and redirect. The ReportRunID is then appended to the session variable keys that need to be unique to each tab. I keep a listing of previous ReportRunIDs to keep track and to clean them out (after a certain time period, or when more than [MAX] ids are encountered) so that I dont overload server memory with these session entries.
Private Sub Page_PreInit(sender As Object, e As System.EventArgs) Handles Me.PreInit
If IsPostBack = False Then
Dim rrid As String = Request.QueryString("RRID")
If ReportRunIDExists(rrid) = False Then
ReportRunID = Now.ToString("_HHmmssfff")
Dim url As String = Request.Url.PathAndQuery
If String.IsNullOrEmpty(rrid) Then
Response.Redirect(String.Format("{0}&RRID={1}", url, _reportRunID))
Else
Dim idx As Integer = url.IndexOf("&RRID=")
Response.Redirect(String.Format("{0}&RRID={1}{2}", url.Substring(0, idx), _reportRunID, url.Substring(idx + 6 + rrid.Length)))
End If
End If
End If
End Sub
Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
'save current RRID/Page/Time to session
UpdateSessionReportRunID(_reportRunID)
End Sub
...
If my bookmarked url looks like:
mysite.com/mypage.aspx?Rpt=123&RRID=_095224678#&&state1=abc&state2=def...
In FF/Chrome/Opera after the redirect, my url looks like:
mysite.com/mypage.aspx?Rpt=123&RRID=_102176253#&&state1=abc&state2=def...
But in IE/Safari after redirect, my url looks like:
mysite.com/mypage.aspx?Rpt=123&RRID=_102176253
Any ideas?
After much more searching, I have come to the realization that History State hash is not sent to the server. It is stored in the querystring so that it is included in bookmarks, but it is accessed by the client-side scriptmanager which causes a postback to load the state values. Since the Server never sees the hash value in the querystring, I have no way of finding those values when a user follows a bookmark to that page.
This problem was introduced as I was trying to start tracking the session state of different browser tabs individually. In my code above, if the RRID parameter is empty or invalid, I have to redirect to self with a new RRID value. When I do that redirect the History State hash was being lost for IE and Safari (but not for the other browsers).
My workaround:
The problem is that I needed to include the hash value in my redirect, but that is not available from the server, so I decided to inject some javascript to the page to perform the redirect from the client where the hash is available.
I already had a Client Redirect extension helper method that I have used in different scenarios, and I modified it to include the current hash value:
<System.Runtime.CompilerServices.Extension()>
Public Sub ClientRedirect(ByVal Response As HttpResponse, ByVal url As String, Optional ByVal target As Target = Nothing, Optional ByVal windowFeatures As String = Nothing, Optional includeCurrentHash As Boolean = False)
If IsNothing(target) Then
If windowFeatures = String.Empty Then
target = ResponseHelper.Target._self
Else
target = ResponseHelper.Target._blank
End If
End If
Dim page As Page = CType(HttpContext.Current.Handler, Page)
url = page.ResolveClientUrl(url)
Dim script As String
script = "window.open(""{0}"", ""{1}"", ""{2}"");"
script = String.Format(script, url & If(includeCurrentHash, "#"" + window.location.hash + """, String.Empty), target.ToString, windowFeatures)
If target = ResponseHelper.Target._self Then
'execute after page has loaded
page.ClientScript.RegisterStartupScript(GetType(Page), "Redirect_" & Now.ToString("HHmmssfff"), script, True)
Else
'execute as page is loading
page.ClientScript.RegisterClientScriptBlock(GetType(Page), "Redirect_" & Now.ToString("HHmmssfff"), script, True)
End If
End Sub
Then in my Page_PreInit where I do the redirects, I changed that to do the ClientRedirect including the current hash, and that has gotten the desired result:
Redirecting the browser while maintaining the History State hash on all browsers.
'redirect from the client so that we keep the History State URL hash
Response.ClientRedirect(String.Format("{0}&RRID={1}", Request.Url.PathAndQuery, _reportRunID), , , True)

ASP.NET / VB.NET Check If a (different) User IsInRole

I have an ASP.NET application on our company's intranet. And a funky security requirement.
I need to check to see if a given username is in a certain role. I cannot use
Page.User.IsInRole("MyDomain\MyGroup")
because
Page.User.Identity.Name
Returns an empty string. Because of some lovely specifications for this program, I have to keep anonymous access enabled in IIS. Seems to rule out any page.user.identity stuff.
So I did find a way to (at least) get the current user (from System.Environment.UserName), but I need to bounce it against the domain group to see if they're in it. Or, better yet, get a list of users within a given domain so I can check myself. Something like...
Dim UserName as String
UserName = System.Environment.UserName
If User(UserName).IsInRole("MyDomain\MyGroup") Then
MyFunction = "Success"
End If
-OR -
Dim GroupUsers as String()
GroupUsers = GetDomainUserNames("MyDomain\MyGroup")
Anybody have any ideas?
You can call IsUserInRole from the Roles static class. Here is a sample and some reference materials.
Roles.IsUserInRole(username, rolename);
link: http://msdn.microsoft.com/en-us/library/system.web.security.roleprovider.isuserinrole.aspx

Resources