Skip to contentSkip to author details

WebForms

A 2-post collection

Adding the [Authorize] attribute to an ASP.NET Web Forms Page control using custom routing

Written by Michael Earls
 ASP.NET  programming  VB  VB.NET  WebForms

I am developing a system that uses custom routing to load pages. I needed a quick way to secure the pages so I didn’t have to put the same

The "old" way

If Not Page.User.Identity.IsAuthenticated Then  
    Response.Redirect("~/Account/Login?ReturnUrl=" & Server.UrlEncode(Request.Url.ToString())
End If

You get the idea.

Anyway, I remember how beautifully WebAPI and MVC handle this by using the “AuthorizeAttribute” attribute. So, I decided to implement this in my page router.

The "new" way

First, you’ll want to enable custom routing in your application as outlined here. Notice that the article I linked to has a solution already outlined for security. I decided not to go that route as I like this solution better.

I have a custom class for handling the IRouteHandler interface implementation required by the custom routing solution.

Within that handler, I do various things like check the virtual paths to make sure that they are valid. Most custom routes get converted to a custom base page that has a public property on it for use with my system. I instantiate that class, set the custom property, and then return that page as the IHttpHandler required by the GetHttpHandler method of the IRouteHandler interface.

Here is the definition of the route in my RouteConfig.vb file:

'Content editor route '  
routes.Add("Edit Content", New Route _  
    ( _ 
        "EditContent/{contentControl}", New CMSPageRouteHandler("~/Pages/CMSContentEditor.aspx") _
     ))

Here is the class definition of a page with the Authorize attribute on it:

<Authorize(Roles:="Editor")> _  
Public Class CMSContentEditor Inherits System.Web.UI.Page  
    ...
End Class

Only users in the Editor role will be able to access this page. Let’s see why.

To start off with, we need to see if our page is decorated with the Authorize attribute. I created a function that does this. I pass in the requestContext that was handed off to me in the GetHttpHandler() method (make sure you Import the System.Web.Http namespace):

Protected Function IsPageAuthorized(ByVal page As Page, ByVal requestContext As System.Web.Routing.RequestContext)  
    Dim authorized As Boolean = requestContext.HttpContext.User.Identity.IsAuthenticated 
    ' Check to see if this page is decorated with the "Authorize" attribute '    
    Dim authAttribute As AuthorizeAttribute = Nothing 
    Dim ptype As Type = page.GetType() 
    For Each attr In ptype.GetCustomAttributes(True) 
        If TypeOf attr Is AuthorizeAttribute Then 
            authAttribute = attr 
            Exit For 
        End If 
    Next 
    If authAttribute Is Nothing Then 
        ' there was no Authorize attribute on this page, so go ahead and let them in '
        authorized = True 
    Else 
        ' see if this page is authorized '
        If Not String.IsNullOrEmpty(authAttribute.Users) Then 
            ' reset the authorized variable since we have Users listed '
            authorized = False 
            If authAttribute.Users.ToLower().Split(New Char() {","}, StringSplitOptions.RemoveEmptyEntries).Contains(requestContext.HttpContext.User.Identity.Name.ToLower()) Then 
                authorized = True 
                GoTo EndOfAuthCheck 
            End If 
        End If 
        If Not String.IsNullOrEmpty(authAttribute.Roles) Then 
            ' reset the authorized variable since we have roles '
            authorized = False 
            ' split the roles into an array '
            Dim roles() As String = authAttribute.Roles.Split(New Char() {","}, StringSplitOptions.RemoveEmptyEntries) 
            If roles.Count > 0 Then 
                For Each role In roles 
                    If requestContext.HttpContext.User.IsInRole(role) Then
                        authorized = True 
                        GoTo EndOfAuthCheck 
                    End If 
                Next 
            End If 
        End If 
    End If 

EndOfAuthCheck:  
    Return authorized 

End Function

First, we set the authorized variable to equal whether or not the user is authenticated. This will save us time later when none of the other checks fail.

After we loop through the attributes on the page, the authAttribute will either be null or it will be equal to the instance of the AuthorizeAttribute that is on the page. If we check it and it’s null, then we know the page is authorized, so we go ahead and set the authorized variable to true and continue on. However, if the authAttribute has a value, then we need to check the properties on the attribute to see if the page is authorized.

User list check

First, we look to see if the User’s name is included in the array of Users (we get it by splitting the Users property of the attribute by commas). I went ahead and converted the Users property to lower case for comparison sake, just in case.

If we find the User’s name in the Users property, then we simply set the authorized variable to true and continue on. I used a GoTo to short circuit and save the work of the next step:

Role checks

Next, we split the Roles into an array, then loop through each one, seeing if the user is in that role. If so, we set the authorized variable to true and continue on.

Authorized or not?

Once all of this has taken place, I return the authorized variable to the caller.

Handling the authorization response

In the calling method, here’s what I do:

If IsPageAuthorized(redirectPage, requestContext) Then  
    Return redirectPage 
Else  
    RedirectToLoginPage(requestContext) 
End If

Here is the RedirectToLogin() method. I add a referrer to the ReturnUrl so I know where the originating request came from. Otherwise, it gets lost when the Login page redirects to the desired page as the Login is now the referrer.

Protected Sub RedirectToLoginPage(ByVal requestContext As System.Web.Routing.RequestContext)  
    ' redirect to the login page, but send the current referrer as a QueryString on the return page so it can be accessed '
    Dim referrer As String = String.Empty 
    If Not requestContext.HttpContext.Request.UrlReferrer Is Nothing Then 
        referrer = "/?referrer=" & requestContext.HttpContext.Request.UrlReferrer.AbsolutePath 
    End If 
    requestContext.HttpContext.Response.Redirect("~/Account/Login?ReturnUrl=" & requestContext.HttpContext.Server.UrlEncode(requestContext.HttpContext.Request.Url.AbsolutePath & referrer)) 
End Sub

Then, in my custom page, I simply check the value of the referrer like so:

If Not Request.UrlReferrer.AbsolutePath.ToLower().Contains("login") Then  
    Session(SESSION_KEY_REFERRER) = Request.UrlReferrer.ToString() 
Else  
    Session(SESSION_KEY_REFERRER) = Request.QueryString("referrer") 
End If

I can’t use ViewState because of issues I’ve run into with custom routing and ViewState with my pages, so I’ve had to resort to using the Session.

As you can see, adding the Authorize attribute to a custom page is easy when you’re doing custom routing. Of course, if custom routing isn’t for you, then this solution won’t work, but I thought I’d share it for those of you who do use custom routes with dynamic pages.

Of "old versus new" - Why not AngularJS? Why ASP.NET?

Written by Michael Earls
 AngularJS  C#  VB.NET  WebForms

I recently commented on a friend’s Facebook post about AngularJS stating that I hoped Angular dies a quick and horrible death. I guess that was a bit harsh, but I have to admit that I’m tired of reading about it. To be fair, I have created an Angular app that used routing, controllers, basic authentication, WebAPI, and messaging, so I do have some experience with it. I liked it, it was nice, and the architecture is sound.

I don’t understand why people are abandoning ASP.NET for SPA (Angular in particular). Are postbacks really that bad? The AJAX-style communication with the server is overrated in my book. If you build a good user interface and have a decent connection, then Angular and ASP.NET should take the same amount of time. Front-loading JS libraries or waiting for the server to respond take the same amount of effort. And, you are more kind to a larger install base of browsers. Angular takes a somewhat arrogant approach to browsers in that it only works on the latest. It is unapologetic about this.  I see much the same attitude from developers of Angular apps, as well. They seem to state that “if you don’t use the latest technology to access my app, then I have no time for you. I’ll be damned if I’m going to use that ‘old’ technology”. Not everyone is like this, but I have seen this attitude on some posts.

Simply using a technology because it’s new is not a good enough reason for me. When I developed my Angular JS app, I immediately saw where I was going to have trouble maintaining it in the future. Upgrades would require a complete rewrite and changes to the structure would require the same architectural challenges as any other technology.

I have Dependency Injection in my current application to help with dependencies, but with Entity Framework being the core of my data story, I don’t have a whole lot of trouble getting to what I need.

In the end, I think it comes down to your comfort level with using “old” technology. In my eyes, it’s a sign that it is tried and true. Just because something is new doesn’t mean it is better. However, looking at it from another angle, there are many new features being added to ASP.NET these days that make life a lot easier for the developer and make the application more efficient.

Of course, you’ll probably be reading another AngularJS post in the future where I’ve decided to use it after all and I couldn’t imagine using anything else.  Maybe for my next project. But for now, I must work with WebForms and VB.NET.