Link to home
Start Free TrialLog in
Avatar of rgrimm
rgrimm

asked on

ASP.NET: User.Identity.Name and User.IsInRole() not getting populated the first time

I am using Forms authentication for my web site. My login page gets the user's login and password.
I look it up in a database, and if everything is OK, then I go ahead and create an authentication
ticket and put it into the cookie (see CODE A below). I then transfer to the main page for the site.

The main page does the Page_Load event code (see CODE B below). If I put a breakpoint at the end of
it and use the Debug window to see what the User.Identity.Name is, and see if the User.IsInRole() is
set, I find that both of them are empty. So it seems like I am failing to get this information into
the ticket (which then populates the IPrincipal info).

So what am I doing wrong?    I do know that I got the data out of the database OK, because I stuff
some of it into session variables and it is there (Session("FName"), for example). It just isn't getting
into the httpContext.Current.User object.

(One more bit of information. If I where to hit Shift+F5 and end the session,
and then hit F5 to restart it, then everything will be OK. However, if I logout, then I am back to
my broken state.)


CODE A   ---   CODE A   ---   CODE A   ---   CODE A

      Dim cookie As HttpCookie
      Dim strHash As String
      Dim strTransferTo As String = "Users/SiteNav.aspx"

         'Create a new forms authentication ticket used with the user's information
         'version = 1
         'name = login name (tbLogin.Text)
         'issueDate = Now
         'Expiration Date = Now + Session timeout minutes
         'IsPersistent = TRUE
         'userData = user's roles in a comma-delimited list
         'cookiepath - not used. MCD is cookieless
         Dim ticket As New FormsAuthenticationTicket(1, _
                                                   tbLogin.Text, _
                                                   Now, _
                                                   Now.AddMinutes(Session.Timeout), _
                                                   True, _
                                                   UserInfo.RoleNames)   ', _
         'FormsAuthentication.FormsCookiePath)

         'Encrypt the ticket and put it into the cookie
         strHash = FormsAuthentication.Encrypt(ticket)
         cookie = New HttpCookie(FormsAuthentication.FormsCookieName, strHash)

         'Set the cookie's expiration time to the ticket's expiration time
         If ticket.IsPersistent Then
            cookie.Expires = ticket.Expiration

            'Add the cookie to the list for outgoing response
            Response.Cookies.Add(cookie)
         End If

         Server.Transfer(strTransferTo)

END CODE A   ---   END CODE A   ---   END CODE A   ---   END CODE A


CODE B   ---   CODE B   ---   CODE B   ---   CODE B   ---   CODE B

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      'Session Variables Available:  EID, Login, LoginID, FName, LName

      'Reset the session timeout
      If Not Page.IsPostBack Then

         Response.AppendHeader("Refresh", Convert.ToString((Session.Timeout * 60) + 10) & "; url=http://www.mychurchdata.com/Login.aspx")
         ifContent.Attributes.Add("Src", "Users/Main.aspx?Return=Postback")

         'Set Login Name
         lblLogin.Text = " " + Session("FName") + " " + Session("LName") + " is logged in."

         'If no current user, then return to default page.
         If Session("Login") = "" Then
            Response.Redirect("../default.aspx")
            Exit Sub
         End If

         'Set menu visibility
         pnlMainMenu.Visible = True
         If User.IsInRole("Administrator") = True Then pnlAdminMenu.Visible = True     'SET BREAKPOINT HERE

END CODE B   ---   END CODE B   ---   END CODE B   ---   END CODE B   ---   END CODE B

SOLUTION
Avatar of testn
testn

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of laotzi2000
laotzi2000

When you use form authentication, the user idtentity is not set automatically by asp .net since it requires anonymous access. Thus you have to populate the user identity yourself.
Avatar of rgrimm

ASKER

(Answering two questions with one post...)

Yes, I do have code in Global.asax. I just forgot to post it or mention it. Here is what I have:

GLOBAL.ASAX CODE ---   GLOBAL.ASAX CODE ---   GLOBAL.ASAX CODE ---   GLOBAL.ASAX CODE

    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires upon attempting to authenticate the use
        'This code assembles the user's credentials on the fly.
        'If there is a current user and they are authenticated and FormsIdentity is being used,
        If Not (HttpContext.Current.User Is Nothing) Then
            If HttpContext.Current.User.Identity.IsAuthenticated Then
                If TypeOf HttpContext.Current.User.Identity Is FormsIdentity Then
                    Dim fi As FormsIdentity = CType(HttpContext.Current.User.Identity, FormsIdentity)
                    Dim fat As FormsAuthenticationTicket = fi.Ticket

                     Dim astrRoles As String() = fat.UserData.Split(",")
                    HttpContext.Current.User = New GenericPrincipal(fi, astrRoles)
                    FormsAuthentication.RenewTicketIfOld(fat)
                End If
            End If
        End If

    End Sub

END GLOBAL.ASAX   ---   END GLOBAL.ASAX   ---   END GLOBAL.ASAX   ---   END GLOBAL.ASAX   ---  

I take it that this code is not populating the user identity, so that code must be missing. Can you show me what code I need and where?
where did u get "UserInfo.RoleNames"?
by the way, did you configure your web.config to be FormsAuthentication?
In your global.asax, your code will never execute. Because your code to populate identity only executes if user.identity is populated. That can never happen. It's a cycle.

The correct way is to get the authentication ticket from cookie, and if that cookie exists, you populate your user.identity as in your code.
Avatar of rgrimm

ASKER

UserInfo is a class that I created. RoleNames is a property of that class that returns a comma-delimited listed of role names which I fetched from the database.
Avatar of rgrimm

ASKER

I put a bunch a breakpoints in my code to track the execution of what happens when:

1. Global.asax/Application_AuthenticateRequest (skips processing of user)
2. Login.aspx/Page_Load, waits for user to enter credentials and press ENTER
3. Global.asax/Application_AuthenticateRequest (skips processing of user)
4. Login.aspx/Page_Load for postback processing (I don't have any)
5. Login.aspx/Btn_Click event where tickets and cookies are created and set
6. Main.aspx/Page_Load, checks for user roles and finds nothing
7. Global.aspx/Application_AuthenticateRequest, finally processes user and roles, but too late for step 6 to use
8. Main.aspx/ read for data entry

So the root problem is that steps 6 and 7 need to be switched, because as it stands the user and user roles are processed after the Main.aspx from has already checked for them. I tried copying the step 7 code into Step 5 (put at the end of CODE A sample above) so it would process the user/roles sooner, but it refuses to do it. It just skips over it. So I am in a quandary. I need step 7 to process before step 6 but it won't.

Any ideas?
Is your code ever executed when you were debugging?
Avatar of rgrimm

ASKER

Yes. The code runs to completion successfully every time.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of rgrimm

ASKER

I have figured out the problem.

The last line of CODE A is:

     Server.Transer(strTransferTo)

It should be:

    Response.Redirect(strTransferTo)

The purpose of CODE A is to prepare the authentication ticket and write it into a cookie and put it on the client. Response.Redirect causes the cookie containing the forms authentication ticket with the user's info and roles to be set on on the client. The Server.Transfer method I used does not do this. Making this one changes causes everything to work.
Avatar of rgrimm

ASKER

Even though neither help directly answered the question, they provided pointers which helped me think through the problem better. The link referenced by Testn is an excellent, comprehensive analysis of how to implement forms authentication. Highly recommended. Laotzi2000's alternate form for authenticating helped me compare two methods of doing the same thing and understand the dynamics of what is going on.

This area of functionality is utterly confusing for people new to ASP.NET. Thanks for helping.