Monday, November 4, 2013

DotNet Forms Authentication with Roles / Groups

The first thing you need to do to use roles or groups with .net FormsAuthentication is to enable FormsAuthentication in the web.config of your website:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 <configuration>
<system.web>

<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" defaultUrl="~/Home.aspx"></forms>
</authentication>

<authorization>
<allow roles="Managers,Sales,Admins" />
<deny users="?"/>
</authorization>

</system.web>
</configuration>


As you can see, I have listed "Managers", "Sales", and "Admins" as roles and just below it I have denied access to any unauthenticated user.

You can also see that my login page is called "login.aspx". On this page, I have a button called: "btnLogin" that the user clicks in order to log in. In my code behind page, I have the following code:

1 2 3 4 5 6 7 8 9 10 Protected Sub btnLogin_Click(sender As Object, e As System.EventArgs) Handles btnLogin.Click
'TODO: Validate username and password here. If valid, continue.

Dim Roles As String = "Managers,Sales,Admins"
Dim Ticket = New FormsAuthenticationTicket(1, "John Doe", Now, Now.AddMinutes(30), False, Roles & "|" & Request.UserHostAddress)
Dim k As New HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(Ticket))
Response.Cookies.Add(k)

Response.Redirect("~/home.aspx")
End Sub


All I'm doing in this code is creating a FormsAuthenticationTicket, encrypting it, and then adding it to the cookies collection that is sent to the user's browser.

I advise you to explore the FormsAuthenticationTicket object so you can understand the arguments I am passing to its constructor. For sake of brevity, I'm only going to focus on the last argument which is "UserData": Roles & "|" & Request.UserHostAddress.

In line #4 I created a list of roles matching the same roles I listed on my web.config file. In this case I'm using commas but any delimiter can be used. I'm also using pipe to separate the user's IP address from the roles. The IP address info is not needed for roles to work. Later, I'll explain why I'm using it.

Now let's go to the Global.asax file to make this baby work:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Protected Sub Application_BeginRequest(sender As Object, e As System.EventArgs)
Try
Dim k = Request.Cookies(FormsAuthentication.FormsCookieName)
If k Is Nothing Then Context.User = Nothing : Return

Dim Ticket = FormsAuthentication.Decrypt(k.Value)
Dim s = Ticket.UserData.Split("|")
If s.Length < 2 Then Context.User = Nothing : Return

Dim IP = s(1)
'To verify that the user IP is the same that created the auth.
If IP <> Request.UserHostAddress Then Context.User = Nothing : Return

Dim Roles = s(0).Split(",")
Dim FormsID = New FormsIdentity(Ticket)
Dim Principal = New GenericPrincipal(FormsID, Roles)
Context.User = Principal
Catch ex As Exception
End Try
End Sub


What this code does is get the authentication cookie that has the encrypted ticket and retrieve the data that I put in there during the login process.

The reason why I'm including the IP address with the roles is because I want to make sure that whoever is making the page request has the same IP address as the person who actually logged in. If you were to hijack the auth cookie, this would add additional protection to prevent you from logging in. Also notice that I'm parsing the roles and adding it to the GenericPrincipal object. This part is what actually makes roles work under FormsAuthentication.

No comments:

Post a Comment