HELP: Authentication code

  • Thread starter Thread starter VB Programmer
  • Start date Start date
V

VB Programmer

PLEASE HELP....

I'm having trouble. In my login form after I've verified the
username/password are valid I do this:
Select Case iMyPrivilege
Case 0
Dim arrRoles() As String = {"guest"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
Case 1
Dim arrRoles() As String = {"guest", "user"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
Case 2
Dim arrRoles() As String = {"guest", "user""admin"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
End Select

In my Global.asax.vb I have this code:
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As
EventArgs)
' Fires upon attempting to authenticate the use
If Request.IsAuthenticated Then
If Context.User.IsInRole("guest") Then
Response.Write("GUEST: " & Context.User.Identity.Name)
ElseIf Context.User.IsInRole("user") Then
Response.Write("USER: " & Context.User.Identity.Name)
ElseIf Context.User.IsInRole("admin") Then
Response.Write("ADMIN: " & Context.User.Identity.Name)
Else
Response.Write("????: " & Context.User.Identity.Name)
End If
End If
End Sub

PROBLEM 1: In Application_AuthenticateRequest the If statement for
"IsInRole" ALWAYS drops to the Else, like it doesn't recognize what I filled
in for form login. Any ideas?

PROBLEM 2: In my Login code I actually had "Context.User =" line outside
the case statement but it kept saying "Name 'arrRoles' is not declared."
even though I did declare it in the case statement. Any ideas?

Thanks!
 
VB Programmer said:
PLEASE HELP....

...
PROBLEM 1: In Application_AuthenticateRequest the If statement for
"IsInRole" ALWAYS drops to the Else, like it doesn't recognize what I filled
in for form login. Any ideas?

Remember that HTTP is stateless, and so is ASP.NET. By the time you get to
Application_AuthenticateRequest, everything you ever did in Login is gone.
You need to persist it, probably in the Forms Authentication ticket. See my
response to your earlier post, "
Question: COntext.User.IsInRole".
PROBLEM 2: In my Login code I actually had "Context.User =" line outside
the case statement but it kept saying "Name 'arrRoles' is not declared."
even though I did declare it in the case statement. Any ideas?

It looks like the case clauses each introduce a new scope. Did you notice
that you were able to declare the same name three times? When that case
clause is done, the scope is gone, and so are any variables declared in that
scope. Declare your array before the "Select" and just set it in each Case
clause.
 
To use forms authentication...

1. Modify <Web.config>
Turn on forms authentication...
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="Login.aspx" />
</authentication>
<authorization>
<deny users="?" />
</authorization>

Insert before the end of the file add the section for Secured dir....
<location path="Secured">
<system.web>
<authorization>
<allow roles="admin"/>
<deny users="*" />
</authorization>
</system.web>
</location>

2. Login.aspx
After user is verified (in db, xml, etc...) add this:

System.Web.Security.FormsAuthentication.RedirectFromLoginPage(txtUserName.Te
xt.Trim, True)

3. Global.asax.vb
First add imports statement "Imports System.Security.Principal"

Then...
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As
EventArgs)
' Fires upon attempting to authenticate the use
If Request.IsAuthenticated Then
' Get the user's role
Dim cnnMyConnection As SqlConnection = New
SqlConnection(ConfigurationSettings.AppSettings("MyDsnString"))
Dim cmdMyCmd As SqlCommand = New SqlCommand("SELECT blah FROM
blah WHERE blah", cnnMyConnection)
Dim drUsers As SqlDataReader

cnnMyConnection.Open()
drUsers = cmdMyCmd.ExecuteReader

While drUsers.Read
Select Case drUsers.GetValue(1)
Case 0 ' guest (read only)
Dim arrRoles() As String = {"guest"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
Case 1 ' user (start/stop engines)
Dim arrRoles() As String = {"guest", "user"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
Case 2 ' admin (everything)
Dim arrRoles() As String = {"guest", "user",
"admin"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
End Select
End While

cnnMyConnection .Close()

'If Context.User.IsInRole("guest") Then Response.Write("GUEST "
& Context.User.Identity.Name)
End If
End Sub
 
VB Programmer said:
To use forms authentication...
....
3. Global.asax.vb
First add imports statement "Imports System.Security.Principal"

Then...
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As
EventArgs)
' Fires upon attempting to authenticate the use
If Request.IsAuthenticated Then
' Get the user's role
Dim cnnMyConnection As SqlConnection = New
SqlConnection(ConfigurationSettings.AppSettings("MyDsnString"))
Dim cmdMyCmd As SqlCommand = New SqlCommand("SELECT blah FROM
blah WHERE blah", cnnMyConnection)
Dim drUsers As SqlDataReader

cnnMyConnection.Open()
drUsers = cmdMyCmd.ExecuteReader

While drUsers.Read
Select Case drUsers.GetValue(1)
Case 0 ' guest (read only)
Dim arrRoles() As String = {"guest"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
Case 1 ' user (start/stop engines)
Dim arrRoles() As String = {"guest", "user"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
Case 2 ' admin (everything)
Dim arrRoles() As String = {"guest", "user",
"admin"}
Context.User = New
System.Security.Principal.GenericPrincipal(User.Identity, arrRoles)
End Select
End While

cnnMyConnection .Close()

'If Context.User.IsInRole("guest") Then Response.Write("GUEST "
& Context.User.Identity.Name)
End If
End Sub

Your code will work fine, and will run on every request made to a page in
your web application. That's a lot of database work.

I suggest you put the database code into Login, save the resultant roles in
the UserData of the Forms Authentication Ticket, and retrieve them in
Application_AuthenticateRequest.
 
Cool. That's basically what I did.

1. What defines where the custom cookie is stored? I used to see the
default cookie in "C:\Documents and Settings\Administrator\Cookies", but now
I can't find my custom cookie?

2. How do I retrieve the roles that are stored in UserData (ticket)?

3. What is a common reason why you would access this in
Application_AuthenticateRequest? This seems to work with no code in
Application_AuthenticateRequest.

You're a great resource! Thanks.
 
Please answer #1 and #2.

Ignore #3: I figured out that this is where you need to setup the new
GenericPrincipal BASED on the role that is stored in the UserData (in the
cookie).... I think. ;)
 
VB Programmer said:
Cool. That's basically what I did.

1. What defines where the custom cookie is stored? I used to see the
default cookie in "C:\Documents and Settings\Administrator\Cookies", but now
I can't find my custom cookie?

If you don't set an expiration date on a cookie, it will be a "session
cookie", which I don't believe is stored on disk. Session cookies are a Good
Thing, as browsers are more likely to be set to accept them than permanent
cookies.
2. How do I retrieve the roles that are stored in UserData (ticket)?

By doing the opposite of of what you did to put them there. :-)

For instance, if your database code in login produced an array of roles, you
might use:

string[] roles = GetRolesForUser(userName);
string userData = String.Join(",", roles);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userName,
System.DateTime.Now,
System.DateTime.Now.AddMinutes(30),
isPersistent,
userData,
FormsAuthentication.FormsCookiePath);

// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);

// Create the cookie.
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName,
encTicket));

// Redirect back to original URL.
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName,isPersistent))
;


Well, in this case you'll want to do the following in
Application_AuthenticateRequest:

FormsIdentity fi = User.Identity as FormsIdentity;
if (fi == null) return; // don't know how _that_ happened!
FormsAuthenticationTicket ticket = fi.Ticket;
string userData = ticket.UserData;
string roles[] = userData.Split(',');
Request.User = new GenericPrincipal(fi, roles);
3. What is a common reason why you would access this in
Application_AuthenticateRequest? This seems to work with no code in
Application_AuthenticateRequest.

But it's not working. If you put the user in a role right now, is he still
in the same role on all subsequent requests? I doubt it. You need to set the
Principal on each request - remember we're talking "stateless".
--
John Saunders
Internet Engineer
(e-mail address removed)
You're a great resource! Thanks.

You're welcome.
 
John, it works like a champ. Thanks for ALL of your help! ;)

FYI, this is what I changed...

(1) In my login page it calls...
Public Sub RedirectFromLoginPage(ByVal strUserName As String, ByVal
strUserData As String, ByVal strDefaultRedirectUrl As String)
Dim ctxMyContext As HttpContext = HttpContext.Current
Dim fatTicket As New FormsAuthenticationTicket( _
1, txtUserName.Text.ToUpper.Trim, DateTime.Now, _
DateTime.Now.AddMinutes(30), False, strUserData)
Dim strCookieValue As String =
FormsAuthentication.Encrypt(fatTicket)
Dim cookieMyCookie As HttpCookie = New
HttpCookie(FormsAuthentication.FormsCookieName)
Dim strReturnUrl As String

With cookieMyCookie
.Path = FormsAuthentication.FormsCookiePath
.Value = strCookieValue
.Expires = DateTime.Now.AddMinutes(30)
End With
ctxMyContext.Response.Cookies.Add(cookieMyCookie)

If ctxMyContext.Request.QueryString("ReturnUrl") Is Nothing Then
strReturnUrl = strDefaultRedirectUrl
Else
strReturnUrl = ctxMyContext.Request.QueryString("ReturnUrl")
End If

ctxMyContext.Response.Redirect(strReturnUrl)
End Sub

(2)
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As
EventArgs)
' Fires upon attempting to authenticate the use
If Request.IsAuthenticated Then
Dim fiIndentity As FormsIdentity = CType(User.Identity,
FormsIdentity)
If fiIndentity Is Nothing Then Exit Sub

Dim fatTicket As Security.FormsAuthenticationTicket =
fiIndentity.Ticket
Dim strUserData As String = fatTicket.UserData

Select Case strUserData
Case "guest"
Dim arrRoles() As String = {"guest"}
Context.User = New
System.Security.Principal.GenericPrincipal(fiIndentity, arrRoles)
Case "user"
Dim arrRoles() As String = {"guest", "user"}
Context.User = New
System.Security.Principal.GenericPrincipal(fiIndentity, arrRoles)
Case "admin"
Dim arrRoles() As String = {"guest", "user", "admin"}
Context.User = New
System.Security.Principal.GenericPrincipal(fiIndentity, arrRoles)
End Select
End If
End Sub
 
This looks good, but one thing was lost in the translation. Ctype doesn't do
the same thing as the "as" operator does in C#.

"object as Type" will return null (Nothing) if object cannot be cast to
Type, otherwise it will do the cast and return the result. On the other
hand, if somehow User.Identify were not a FormsIdentify, CType would throw
an exception.
 
The reason I changed it from...
Dim fiIndentity As FormsIdentity = User.Identity
to...
Dim fiIndentity As FormsIdentity = CType(User.Identity, FormsIdentity)
....was that I go a squiggly under User.Identity stating "Option Strict On
disallows implicit conversions from 'System.Security.Principle.Iidentity' to
'System.Web.Security.FormsIdentity.'" Should I do it an alternate way?
 
VB Programmer said:
The reason I changed it from...
Dim fiIndentity As FormsIdentity = User.Identity
to...
Dim fiIndentity As FormsIdentity = CType(User.Identity, FormsIdentity)
...was that I go a squiggly under User.Identity stating "Option Strict On
disallows implicit conversions from 'System.Security.Principle.Iidentity' to
'System.Web.Security.FormsIdentity.'" Should I do it an alternate way?

No, but you should first check to make sure it's a FormsIdentity - and I
can't remember right now how VB.NET does that!
 
Back
Top