Link to home
Start Free TrialLog in
Avatar of Brian
BrianFlag for United States of America

asked on

ASP.NET Authentication based on Access Permissions

Hi EE,

I'm looking for some help on finishing some code that i have. Basically i would lke to redirect a user to his/her appropriate page based on their access permissions that are stored in the database.

I'm going to attach the code that i have now that authenticates by username and password Hash and Password Salt but need help with the rest. I think i need a Select Case but not sure how to implement that into what i have now.

Thanks in advance!!!

Public Function gfCreatePasswordHash(ByVal vstrPwd As String, ByVal vstrSalt As String) As String
        Dim strSaltAndPwd As String = String.Concat(vstrPwd, vstrSalt)
        Dim strHashedPwd As String = FormsAuthentication.HashPasswordForStoringInConfigFile(strSaltAndPwd, "SHA1")
        Return strHashedPwd
    End Function
 
    Protected Sub btnAuthenticate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAuthenticate.Click
 
        Dim strSaltFromDB As String
        Dim hashPwdInDB As String
        Dim hashPwdEnterd As String
 
        Session("username") = txtUsername.Text
 
        Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString
        Dim conn As SqlConnection = New SqlConnection(connectionString)
 
        Dim comm As SqlCommand
 
        comm = New SqlCommand("AuthenticateLogin", conn)
        comm.CommandType = System.Data.CommandType.StoredProcedure
 
        comm.Parameters.Add("@username", SqlDbType.VarChar, 50)
        comm.Parameters("@username").Value = txtUsername.Text
 
        Try
            conn.Open()
            Dim myreader As SqlDataReader
            myreader = comm.ExecuteReader(CommandBehavior.CloseConnection)
            If myreader.Read() Then
 
                hashPwdInDB = myreader("passwordhash").ToString
                strSaltFromDB = myreader("passwordsalt").ToString
 
                hashPwdEnterd = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)
                If hashPwdEnterd = hashPwdInDB Then
                    FormsAuthentication.RedirectFromLoginPage(txtUsername.Text, True)
 
                Else
                    lblError.Text = "your login credentials are invalid!!!"
                End If
            End If
            myreader.Close()
        Catch
            lblError.Text = "the pssa database is currently offline."
        Finally
            conn.Close()
        End Try
 
    End Sub

Open in new window

Avatar of guru_sami
guru_sami
Flag of United States of America image

Since you did not provide much info on "access permissions that are stored in the database" here is what I think.
Modifications you need is here:

If hashPwdEnterd = hashPwdInDB Then
  'Note the use of SetAuthCookie
         FormsAuthentication.SetAuthCookie(txtUsername.Text, True)
  ' Get Access Rights From DB
  ' Redirect the user based on access right
  if access right = edit
        Response.Redirect("Edit.aspx")
if access right = Readonly
         Response.Redirect("Readonly.aspx")
...and so on
   
                   Else
lblError.Text = "your login credentials are invalid!!!"
End If

Now the problem here is....if any authenticated user who knows the url of your pages can navigate directly to those pages by typing the url in browser address bar..
i.e. say you redirect user to Readonly.aspx but if the user types in the address bar edit.aspx they will be given access to since they are authenticated.

So in that case your next best option would be using Roles.
Avatar of Brian

ASKER

Hi guru_sami,

Sorry, the field name in my DB for access permissions is called accessperm and it's an int. Now if i wanted to use the name instead of int would i need to create an inner join in my SP that retrieves the username, passwordsalt, passwordhash?

Current SP:

ALTER PROCEDURE [dbo].[AuthenticateLogin]
@username varchar(50)
AS
SELECT username, passwordhash, passwordsalt FROM EmpSalt WHERE username = @username

TABLE with Access Permissions:

Table Name = ACPER
id = int  PK
acid = int  ID for Access Permissions
acname = varchar Name of Access Permission (Admin, Member, User)
I would suggest you to set up roles table instead of what you have. This will make things more easier and meaningful.
Check here: http://aspnet.4guysfromrolla.com/articles/082703-1.aspx
Follow the article and you will get better idea.
Avatar of Brian

ASKER

yeah, i have a book that talks about that but was wondering how to do it from a programming stand point instead.
Avatar of Brian

ASKER

ok, i modified the following below but i'm not recieving any errors when i run it, HOWEVER i'm not going anywhere based on the access permission either :(

Also, I modified my web.config file using <location> on each page unless the user is successful in logging in they can't access.

Protected Sub btnAuthenticate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAuthenticate.Click

        Dim strSaltFromDB As String
        Dim hashPwdInDB As String
        Dim accessright As Integer
        Dim hashPwdEnterd As String

        Session("username") = txtUsername.Text

        Dim connectionString As String = ConfigurationManager.ConnectionStrings("DemoSql").ConnectionString
        Dim conn As SqlConnection = New SqlConnection(connectionString)

        Dim comm As SqlCommand

        comm = New SqlCommand("AuthenticateLogin", conn)
        comm.CommandType = System.Data.CommandType.StoredProcedure

        comm.Parameters.Add("@username", SqlDbType.VarChar, 50)
        comm.Parameters("@username").Value = txtUsername.Text

        Try
            conn.Open()
            Dim myreader As SqlDataReader
            myreader = comm.ExecuteReader(CommandBehavior.CloseConnection)
            If myreader.Read() Then

                hashPwdInDB = myreader("passwordhash").ToString
                strSaltFromDB = myreader("passwordsalt").ToString

                hashPwdEnterd = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)
                If hashPwdEnterd = hashPwdInDB Then
                    'Note the use of SetAuthCookie
                    FormsAuthentication.SetAuthCookie(txtUsername.Text, True)
                    ' Get Access Rights From DB
                    ' Redirect the user based on access right
                    accessright = myreader(CInt("acperid")).ToString
                    If accessright = 10 Then
                        Response.Redirect("Edit.aspx")
                    End If
                    If accessright = 11 Then
                        Response.Redirect("Readonly.aspx")
                    End If
                End If
set breakpoints on If here and see if accessright is assigned correct value.
Also I see accessright is an int and you are assigning it a String value....
 accessright = myreader(CInt("acperid")).ToString
                    If accessright = 10 Then
                        Response.Redirect("Edit.aspx")
                    End If
                    If accessright = 11 Then
                        Response.Redirect("Readonly.aspx")
                    End If
Avatar of Brian

ASKER

I didn't see anything after setting a breakpoint I changed to the following below.

Dim accessright As Integer = Convert.ToInt32(myreader("acperid"))

                hashPwdEnterd = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)              
                If hashPwdEnterd = hashPwdInDB Then
                    'Note the use of SetAuthCookie
                    FormsAuthentication.SetAuthCookie(txtUsername.Text, True)
                    ' Get Access Rights From DB
                    ' Redirect the user based on access right
                    accessright = myreader(CInt("acperid")).ToString
                    If accessright = 10 Then
                        Response.Redirect("Edit.aspx")
                    End If
                    If accessright = 11 Then
                        Response.Redirect("Readonly.aspx")
                    End If
                End If
what value is assigned to "accessright " ...is correct value returned from DB?
Avatar of Brian

ASKER

acperid, yes it is a "int" field in the DB and has a value assigned to it. The only values for acperid in the DB are 10 and 11.
Yes but when you do this:
Dim accessright As Integer = Convert.ToInt32(myreader("acperid"))

What value is assigned to accessright is it 10 or 11 or something else
i.e. myreader("acperid") is getting right value when you execute the SP.
Avatar of Brian

ASKER

it would be either 10 or 11

when i execute my SP and when i enter a username it displays everything along with the acperid for example: username: jdoe would return 10 as the acperid
I do understand it would return either 10 or 11 but when debug and you login as a particular user say jdoe...what is it returning?

Consider the code below:

                hashPwdEnterd = gfCreatePasswordHash(txtPassword.Text, strSaltFromDB)              
                If hashPwdEnterd = hashPwdInDB Then
                    'Note the use of SetAuthCookie
                    FormsAuthentication.SetAuthCookie(txtUsername.Text, True)
                    ' Get Access Rights From DB
                    ' Redirect the user based on access right
      Dim accessright As Integer = Convert.ToInt32(myreader("acperid"))

-->                  If accessright = 10 Then
                        Response.Redirect("Edit.aspx")
                    End If
-->                    If accessright = 11 Then
                        Response.Redirect("Readonly.aspx")
                    End If
                End If

When you set breakpoints at --> and say you have loggedin as jdoe ...hover over the accessright and see what value are you getting.
If say you are getting 10 ...then on pressing F11 does the control move to  Response.Redirect("Edit.aspx") ?
or where does the control go.
Avatar of Brian

ASKER

ok i set breakpoints at the places you mentioned and yes the value i'm getting for accessright is 10 and when i press f11 the control moves to response.redirect("edit.aspx").
So then aren't you redirectd to edit.aspx?
i mean do you have a page called edit.aspx?
so Response.Redirect(url);
url is where you want the user to get redirected if  accessright = 10?
Or do you stay on the same page ?
Can you tell what is happening i.e. do you still stay authenticated or something else happening...
Avatar of Brian

ASKER

guru_sami,

Ok, i did make a mistake with the values 10 and 11 it was 1 and 2 :( I'm very sorry.

That part works now i have another issue that is related to this login though. If i navigage to those pages i get send to the login page which is GREAT, however no matter what the acperid is it lets me in, i want to restrict based on the value such as 1 or 2. See how my web.config is.

Do i need to add the code below somewhere?

Dim ticket As FormsAuthenticationTicket = New FormsAuthenticationTicket(1, USER_USERNAME.Text, DateTime.Now, DateTime.Now.AddMinutes(30), True, "1", FormsAuthentication.FormsCookiePath)

Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))

Response.Redirect("1.aspx")



Dim ticket As FormsAuthenticationTicket = New FormsAuthenticationTicket(1, USER_USERNAME.Text, DateTime.Now, DateTime.Now.AddMinutes(30), True, "2", FormsAuthentication.FormsCookiePath)

Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))

Response.Redirect("2.aspx")



Dim ticket As FormsAuthenticationTicket = New FormsAuthenticationTicket(1, USER_USERNAME.Text, DateTime.Now, DateTime.Now.AddMinutes(30), True, "3", FormsAuthentication.FormsCookiePath)

Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
Response.Cookies.Add(New HttpCookie(FormsAuthentication.FormsCookieName, encTicket))

Response.Redirect("3.aspx")


  <location path="admin/index.aspx">
    <system.web>
      <authorization>
        <deny users="?"/>
      </authorization>
    </system.web>
  </location>

  <location path="member/index.aspx">
    <system.web>
      <authorization>
        <deny users="?"/>
      </authorization>
    </system.web>
  </location>

  <location path="user/index.aspx">
    <system.web>
      <authorization>
        <deny users="?"/>
      </authorization>
    </system.web>
  </location>
ASKER CERTIFIED SOLUTION
Avatar of guru_sami
guru_sami
Flag of United States of America image

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 Brian

ASKER

Hi guru_sami,

Ok, i implemented everything from above, HOWEVER if i load admin/index.aspx page it redirects me back to my login (which is what i needed it to do) BUT when i login with a different set of credentials it redirects me to the appropriate page. I would like to display a message such as "Invalid Credentials" if a user tries to navigagte to a page and has to login before accessing.

Also, If a username is correct but password is bad then it still directs me to the page as if both username and password and accesspermissions where correct.
Not sure if I got that part...can you explain with example / flow..about what behavior you are seeing
Avatar of Brian

ASKER

If i naviage to members/index.aspx and run the page without logging in first it redirects me to the main login.aspx page (which works as I needed to prevent unauthorized access without first logging in). However, if i where to enter credentials for access to admin/index.aspx instead of credentials for members/index.aspx then it redirects me to admin/index.aspx instead of displaying an error message stating that I entered incorrect credentials for members/index.aspx.

The main login.aspx page works as i needed which redirects the user to the appropriate page based on there credentials thanks to you!!
did you set the roles properly in your web.config?
Can you share that portion.
I see whats happening....need some modification in the login code...

'store any returnUrl queryStringParam in a variable
Dim returnUrl As String = Request.Params("ReturnUrl")
If accessright = 10 Then
     If returnUrl IsNot Nothing Then
         Response.Redirect(returnUrl.ToString)
     Else
         Response.Redirect("Edit.aspx")
     End If
End If

'same for other respective accessright check.

Now in your Login Page Load add this check:

If Not Page.IsPostBack Then          
If Request.IsAuthenticated AndAlso Not String.IsNullOrEmpty(Request.QueryString("ReturnUrl")) Then              
 ' This is an unauthorized, authenticated request...                
lblError.Text = "UnAuthorized Access!"        
 End If      
End If

Hope that should do...
Avatar of Brian

ASKER

No, still not working right. Now when i run member/index.aspx it redirects to the login.aspx page which is what i need, however now when i login at the login.aspx page to verify my credentials to access the member/index.aspx page if I enter credentials that are in the DB it will still redirect me to the member/index.aspx page even if that user is not in that particular role.
can you attach the code you have so far...
Avatar of Brian

ASKER

Ok, i'm going to attach what i have so far.
EE-Code.txt
Try accessing membmes/index.aspx page....login with non-member credentials i.e say admin user...
set breakpoints at following lines in your code and debug:
1-->If returnUrl IsNot Nothing Then
2-->                   Response.Redirect(returnUrl.ToString)
                        Else
3-->                    Response.Redirect("admin/index.aspx")
      End If

PageLoad
If Not Page.IsPostBack Then
         If Request.IsAuthenticated AndAlso Not String.IsNullOrEmpty(Request.QueryString("ReturnUrl")) Then
                ' This is an unauthorized, authenticated request...                
4-->               lblError.Text = "UnAuthorized Access!"
            End If
        End If

Now what to note:
1: Value of returnUrl variable
2: Do you hit breakpoint 2 or 3 or none
3: If you hit breakpoint 2 you should hit breakpoint 4

Also you did not share your global.aspx code and web.config settings in your attachement. Please share that as well.




Avatar of Brian

ASKER

Ok, i'm attaching a text file with the results of the breakpoints and my web.conifg and global.asax code.
EE-CODE-1.txt
You have <deny users="?"/> in your web.config in location section .. that is the reason you are not hitting breakpoint 4
It should be <deny users="*"/> i.e. star
Check my previous post ID - 24768272 ...
So it should be like this:
<location path="admin/index.aspx">
    <system.web>
      <authorization>
        <allow roles="Admin"/>
        <deny users="*"/>
      </authorization>
    </system.web>
  </location>

Try this and let me know how it goes...i.e run the previous test and see if you hit breakpoint 4.
Avatar of Brian

ASKER

ok, i changed <deny users="?"> to <deny users="*"> and i went through the breakpoints again but it never hits breakpoint 4 only breakpoint 1 & 2 and then it goes to my Catch in 'Try Catch Finally' and then it opens the members/index.aspx as if i logged in with the correct credentials for this page which i did not.
how about running the same test again .... but put this check in your members/index.aspx page Load..
If User.IsInRole("Admin") Then
5 -->    Response.Redirect("admin/index.aspx")
End If

Set one more breakpoint as  5 above.
Also you say you are hitting Catch... can you change your it to like this and set breakpoint 6:
 Catch ex As Exception
6-->    Response.Write(ex.Message.ToString())

I think there is some small miss which I am not able to catch.
Also just want to confirm you did change
<deny users="?"/> to
 <deny users="*"/>
for members/index.aspx location as well right?

Oh one more thing I see in your code:
you have
<location path="member/index.aspx">
I believe it should be
<location path="members/index.aspx"> // you see missin 's' in 'member'

Also I would recommend changing admin/index.asp and members/index.aspx path to just like below. It is just an enhancement and not related to the problem you are seeing.
<location path="admin">
<location path="members">
       
Avatar of Brian

ASKER

ok, after implementing everything that you provided from above then the application is now working as i needed it to. I did not have <deny users="?"> so i made that change and ran it and all was fine, however i have a few questios to ask below.

1.) Do i really need a global.asax file? If so, why?
2.) Do i need the code for member/index.aspx Page_Load that checks for User.IsInRole?
3.) Do i still need the code for Page_Load in login.aspx? If so, what does this actually do then?
1.) Do i really need a global.asax file? If so, why?
Yes that is required...because it is used by asp.net authorization module to make decision based on roles i.e. <allow roles="Admin"/>. So everytime you access a page the urlAuthorization Module will check against the roles for current logged-in user that was added here:
HttpContext.Current.User = New GenericPrincipal(id, roles) //in global.aspx pages.

2.) Do i need the code for member/index.aspx Page_Load that checks for User.IsInRole?
No that was for troubleshooting purpose to determine if your Roles are working properly. This work is already done for you by UrlAuthorizationModule. Also User.IsInRole check was possible due to code in global.aspx. So you can remove this check.

3.) Do i still need the code for Page_Load in login.aspx? If so, what does this actually do then?
Yes that code is required. That code is for setting your labelText. If you remove that code, you will just be redirect to login.aspx without any message.
So this is what is happening:
a)you try to navigate to members/index.aspx
b)you will be redirected to login.aspx with returnUrl=members/index.aspx
c)now you login as admin ..after successful user credentials validation...you hit this line:
   Response.Redirect(returnUrl.ToString)
d)you are redirected to members/index.aspx since returnUrl is not null
e)but asp.net urlauthorization module check this from your web.config
<allow roles="Member"/>
        <deny users="*"/>
Oh the user is not in Member role(because user is in Admin Role)...so moves to next deny ..i.e. it will deny everyone else..
f)So you will be redirected to login.aspx with returnUrl=members/index.aspx.
g) in Login_Pageloge there is a check is Request Authenticated(it will be yes...because admin user was authenticated but not authorized to view member area) and returnUrl is not null....so set the labelText.

Hope this answers all your questions.

Avatar of Brian

ASKER

Thank you VERY MUCH for all your help. I never would have figured this out without your help. Is there a way that i could learn more about this type of stuff? If so where should i start?

Thanks again for all you did!!!