# Friday, March 14, 2008
The ASP.NET 2.0 DefaultButton property on Form and Panel allows IE to fire a button or link when the user hits the enter key while in a form field.  The LinkButton control does not fire when the users browser is FireFox.  Several people have found creative ways to solve this problem.  I chose to take advantage of .NET 3.5 support for Extension Methods and just wrap the needed fixes into a method right on my form object.  Here is the code for you to drop into your own library.  Perhaps I will do the same for Panel when the need arises.
  
 public static class BrowserHelper
    {
         public static void DefaultLinkButton(this HtmlForm form, LinkButton defaultButton)
        {
            // Inspired by: http://kpumuk.info/asp-net/using-panel-defaultbutton-property-with-linkbutton-control-in-asp-net/

            // Wire-up the Link Button default supported in ASP.NET
            form.DefaultButton = defaultButton.UniqueID;

            // Script to wireup a click event for FireFox
            string _addClickScript = "addClickFunction('{0}');";

            string _addClickFunctionScript =
                @"  function addClickFunction(id) {{
            var b = document.getElementById(id);
            if (b && typeof(b.click) == 'undefined') b.click = function() {{
                var result = true; if (b.onclick) result = b.onclick();
                if (typeof(result) == 'undefined' || result) {{ eval(b.href); }}
            }}}};";

            form.Page.ClientScript.RegisterStartupScript(typeof(LinkButton), "addClickFunctionScript", _addClickFunctionScript, true);

            // Execute the script when the Page loads
            string script = String.Format(_addClickScript, defaultButton.ClientID);
            form.Page.ClientScript.RegisterStartupScript(typeof(LinkButton), "click_" + defaultButton.ClientID, script, true);
        }
    }
Friday, March 14, 2008 11:51:41 AM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  | 
# Monday, July 02, 2007
When hosting Google Video's on your site it can be handy to pull back the thumbnail for the video image. Currently there is no API for doing this so I wrote a method to do it for me. First I retrieve the RSS feed for the video. Then I pull out the thumbnail path from that feed.

        private static string GetGoogleThumnailPath(string contentID)
        {
            string path = null;
            // Fetch the RSS for the video
            WebRequest req = HttpWebRequest.Create("http://video.google.com/videofeed?docid=" + contentID);
            req.Method = "GET";
            WebResponse response = req.GetResponse();
            XmlTextReader stream = new XmlTextReader(response.GetResponseStream());
            XPathDocument doc = new XPathDocument(stream);
            XPathNavigator nav = doc.CreateNavigator();
            XmlNamespaceManager manager = new XmlNamespaceManager(nav.NameTable);
            manager.AddNamespace("media", "http://search.yahoo.com/mrss/");
            XPathNodeIterator iterator = nav.Select("/rss/channel/item/media:group/media:thumbnail/@url", manager);
            while (iterator.MoveNext())
            {
                path = iterator.Current.Value;
            }

            return path;
        }

We used this code on the Pets Best Community site. By the way if you are interested in Pet Insurance you can use my Promo Code: CISAKSON5 for a 5% discount. If you do not see the box to enter it on the Get a Quote page then it probably has automatically been applied through one of my links.
Monday, July 02, 2007 5:25:46 PM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  | 
# Tuesday, September 05, 2006

Today I received an update to a suggestion I made to Microsoft on April 8, 2005.  http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=102180  That was before Beta 2 of Whidbey if memory serves me right.  My suggestion was to add features to the Membership API that would allow for a user to be renamed.  There are many scenerios where we choose to use the full Email address of a user as their username instead of having a seperate username.  This works great until the user changes their Email address.  (Another reason to have a permanent address that is not affiliated with your ISP)  I need to be able to allow users to update their Email address and thus their username/userid. 

Apparently my suggestion has been considered for Orcas, but isn't going to make it.  Still, I thought it was good that it was at least considered.

In the meantime, I have found that I can rename a user by directly updating the membership database.  Microsoft cautions against this when using multiple stores for Membership, Roles, and Profile data because user + application is the identity that is shared across disparate stores.  It is my opinion that they should have used a unique identifier instead.  The MembershipUser supports such an identity already.  Similiarly I wish Windows authenticated users and forms authenticated users shared a common data type for their identities.  I believe Microsoft would say that indeed they do and this is the string type that the username or User.Identity.Name.  For my purposes I created a database table to map a GUID to Windows identities so that I could normalize all of my UserId's to be unique and meaningless.  A Email address as a username carries meaning.  That makes a bad primary data key in many scenarios because of the lack of good support for renaming.  Keys should never need renaming.  Even a Windows UserId can change when its based on a persons name and their legal name changes.

What do you all think?

 

Tuesday, September 05, 2006 9:35:09 PM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, July 18, 2006

I just finished blowing an afternoon fighting an issue in ASP.NET 2.0 and crawler detection.  There is a slick feature where you can define browser capabilities with .browser files.  My tests were indicating that Yahoo (Slurp) and Ask (Teoma) were not being detected as crawlers by the framework.  Simple enough.  I'll just create a .browser file and add it to App_Browsers in my app.  In the file I will do a User Agent match on Slurp and Ask. 

A few hours pass by.....its not working....dive into reflector....find some hard coded string having to do with crawler detection.

Take a look at System.Web.Configuration.BrowserCapabilitiesFactory.CrawlerProcess(NameValueCollection headers, HttpBrowserCapabilities browserCaps).  In there is a curious little string "crawler|Crawler|Googlebot|msnbot" being passes to a RegEx processor.

It looks like Yahoo and Ask crawlers are detected as Mozilla browsers, but not as crawlers.  Google and MSN on the other hand are detected as both.  Curiously Google and MSN are both in the little hard coded string that is compiled into the assembly.

My theory is that the browser capabilities detection sees these popular crawlers as Mozilla browsers first and then fails to detect that they are crawlers.  Only those with a user agent string matching what is coded in the framework also get flagged as crawlers.  If anyone has more insight, please comment.

So, to fix the issue I ended up implementing my own wrapper.  Here you go:

      public static bool IsCrawler(HttpApplication app)
      {
         bool isCrawler = app.Context.Request.Browser.Crawler;
         // Microsoft doesnt properly detect several crawlers
         if (!isCrawler)
         {
            Regex regEx = new Regex("Slurp|slurp|ask|Ask|Teoma|temoa");
            isCrawler = regEx.Match(app.Request.UserAgent).Success;
         }
         return isCrawler;
      }

Tuesday, July 18, 2006 5:54:01 PM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [12]  | 
# Sunday, June 18, 2006

Having just returned from TechEd 2006 I am excited to begin sharing my passion for Windows Workflow Foundation (WF).  It is my opinion that WF is the tool that will enable model driven development that will become the de facto standard for application and system development.  If you are not familiar with WF yet, then its time to start reading up on it over at www.netfx3.com.  Currently Microsoft is shipping WF beta 2.2 with both Sequential and State Machine workflow activities.  Later this year they are planning to release WF and a CTP of a new WF activity know as PageFlow.  While you wait you might check out the Webcast from Israel Hilerio to see some of the ideas going into the page flow activities.  I really enjoyed meeting Israel and the other members of the WF team as well as Kashif Alam.  Kashif is the point of contact for the ASP.NET team when it comes to WF integration and the coming Page Flow activities.

I have spent the last few weeks trying to build a page flow model on top of a State Machine since I was not aware of the model Microsoft is working on.  Now that I have a better understanding of where Microsoft is heading as well as a working prototype of my own I would like to begin sharing my code and ideas.  I look forward to other feedback and ideas for improvement.  I am ready to use WF now!  My hope is that, together with anyone who reads this, we can create and maintain a set of tools that will enable powerful WF solutions underneath very thin UI layers. 

I will be focusing on starting a WF powered web application from scratch.  What will make it different from a traditional application is that all logic and flow will be driven by the WF engine, activities, and abstraction layers.  In the end I will have a web UI that is completely brainless.  All short and long term persistence will be managed by WF activities and WF code.  All navigation and page flow will be driven by WF models, yet equally supportive of non-linear processes and browser back buttons.  Running a Windows UI on top of the workflow will be fully supported, though at this time I do not plan to explore that scenario, except to ensure that system.web is not referenced anywhere except the UI layer.  I will be demonstrating an ASP.NET WorkflowManager (WFM), but it should be trivial to create a similar Smart Client WorkflowManager as well.  The WFM will by the abstraction layer between the UI and the WF making it simple for the developer to pass objects between the 2 and manage the runtime and WF instances.

In addition I will share my implementation of the Simple Read and Write activities that were introduced in the MS Help Desk sample application.  The Help Desk sample application was also written by Israel Hilerio to demonstrate an option for simplifying the sharing for data between a workflow and the hosting application.  I have found it to be very effective for many scenarios and I look forward to presenting some enhancements to it for enabling additional scenarios.  These will be incorporated into the WFM and added to the WF Runtime as DataExchange services.

I look forward to interacting with the WF community so fell free to introduce yourself and comment with questions as I post articles and code.

 

Sunday, June 18, 2006 9:19:04 PM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  | 
# Friday, March 03, 2006

This week I started reading Professional ASP.NET 2.0 Security, Membership, and Role Management by Stefan Schackow.  I can't recommend this book enough.  Stefan is a great guy as well.  I had opportunity to meet him on a couple different visits to Redmond.  I wish I had this book 2 years ago when I started looking at the Membership and Role API's in .NET 2.0.  (Yes, I was looking these things 2 years ago.  They were announced at the PDC in Oct. 2003).

Today I found a bonus nugget of goodness I had to share that is indirectly related to ASP.NET 2.0 security.  On Page 429 Stefan explains why all the SQL objects in the Microsoft SQL providers for .NET 2.0 explicitly reference DBO as the object owner.  (ex. dbo.aspnet_Memberhsip)  It turns out that SQL 2000 takes a performance hit if the object owners are not explicitly declared in stored procedures, views, etc.  The performance hit is significant enough that Microsoft did a QFE on ASP.NET 1.1 SQL Session state.  I had no idea and I figured many others also were clueless about the patch for Session State as well as the performance issue.

Check out Todd Carter's blog for some more details.  It also includes a link to the .NET 1.1 Session State fix.

Friday, March 03, 2006 10:11:53 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, January 11, 2006

Previously I blogged about possible solutions for tracking downloads with Google Analytics.  The solution I am going to experiment with first is to dynamically add the tracking code to my download links via JavaScript.  I spent quite a bit of time on my script to make it cross-browser compatible and so that it would handle proper timing of execution based on the loading of the links.  I also used a regular expression to limit the tracking to specific link types.  Hopefully those match most cases.  If all goes well I will probably wrap all of this into a simple ASP.NET control that can be added to an page you want tracked. 

First off you need the standard Google tracking scripts:

<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script>
<script type="text/javascript">
_uacct = "UA-XXXXXX-X";
urchinTracker();
</script>

Second is my new download tracking script.  I have placed it in a separate file for browser caching and easy updating.  Also it has the defer="defer" attribute so that IE will not load the script until the content has finished loading.  XHTML compliance required me to include a value for the attribute.  Please let me know if you come up with any improvements to the script or if you find any errors.  Thanks to quirksmode for info on the dynamic event models and dean edwards for info on deferred script execution.

<script type="text/javascript" src="downloadtracker.js" defer="defer"></script>

Here is the actual downloadtracker.js code

// We dont want the try adding the tracking code until the page links are loaded
if (document.addEventListener) {
document.addEventListener(
"DOMContentLoaded", addEvents, null); // Firefox
} else {
addEvents();
// IE : Call the function immediately because the script is referenced with the defer attribute supported by IE
}

function addEvents()
{
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
for (i=0; i <document.links.length; i++)
{
var x = document.links[i];
// Only attach tracking code to specific file types
var extensions = new RegExp(".+\.(zip|pdf|xls|doc|csv|txt|ppt|xml|rtf)$");
var doc = x.href.toLowerCase().match(extensions);
if (doc)
{
if (x.attachEvent)
{
x.attachEvent(
'onclick', function () {TrackIt(window.event.srcElement)}); // IE
} else {
x.addEventListener(
'click', function () {TrackIt(this)}, false); // Firefox
}
}
}
}

function TrackIt(link)
{
// Remove the conversion to Lowercase if you are on a Case sensitive web server
urchinTracker(link.href.toLowerCase());
}

Wednesday, January 11, 2006 12:02:45 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  | 
# Wednesday, November 23, 2005

It seems like forever ago that I started recommending Fredrik Normen's Permission Manager.  Recently I was tasked to find a way to restrict nodes in a SiteMap based on Permission.  Today I completed that task and thought I would share it with the world.  First of all, I appoligize for writing the code in VB.NET (employers standard), but you can all compile it and reference it from C# so get over it.

What I did to create the PermissionXmlSiteMapProvider was to inherit directly from XmlSiteMapProvider and then extend 2 methods.  First of all I extended the Initialize method so that I could verify that it was configured properly.  Since it is dependent on the presence of Permission Manager and it properly working I added a simple check to verify that it has a provider.

The second method to override for the addtion of Permission to a SiteNode was IsAccessibleToUser.  Thankfully the siteNode can have extra attributes on it without complaint.  I used that to check for the permission attribute.  If it is there then a call to Permission Manager is done for the named permission and the currently logged in user.

After configuring my applications to use my new Provider I restrict access to nodes as easy as this:

<siteMapNode url="Edit.aspx" title="Edit" permission="Modify" />

Since I built on top of the XmlSiteMapProvider I also get the ability to do Roles restriction and a combination of Roles and permission:

<siteMapNode url="Delete.aspx" title="Delete" Roles="Admins, Editors" permission="Delete" />

It is important to note a few things that I ran into while working on this solution.  You must enable Security Trimming.  If you will restrict based on Roles you also must enable RoleManager.  Restricting via Roles is a little quirky as described in the MSDN Site Map Providers document.  Specifically review the Security Trimming section.

Here is the siteMap section of Web.config that implements the PermssionXmlSiteMapProvider:

<siteMap enabled="true" defaultProvider="PersmissionXmlSiteMapProvider">
  <
providers>
    <
add name="PersmissionXmlSiteMapProvider
           
type="IDCL.Web.Security.PersmissionXmlSiteMapProvider, IDCL.Web"
           
description="Permission xml site map provider"
          
securityTrimmingEnabled="true"
          
siteMapFile="Web.sitemap"/>
  providers>
siteMap>

Obviously you also need to have Permission Manager installed and configured or this will not work.

Here is the code for the provider:

Imports System
Imports System.Web
Imports System.Configuration.Provider
Imports System.Web.Configuration
Imports Nsquared2.Security

Public Class PersmissionXmlSiteMapProvider
Inherits XmlSiteMapProvider

Public Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)

' Verify that config isn't null
If config Is Nothing Then
  Throw New ArgumentNullException("config")
End If

' Verify that Permission Manager is configured
If PermissionManager.Provider Is Nothing Then
  Throw New ProviderException("Permission Manager default provider missing.")
End If

' Add a default "description" attribute to config if the
' attribute doesn't exist or is empty
If String.IsNullOrEmpty(config("description")) Then
  config.Remove("description")
  config.Add(
"description", "PermissionXmlSiteMap provider")
End If

' Call the base class's Initialize method
MyBase.Initialize(name, config)
End Sub

Public Overrides Function IsAccessibleToUser(ByVal context As System.Web.HttpContext, ByVal node As System.Web.SiteMapNode) As Boolean

  Dim authorized As Boolean = MyBase.IsAccessibleToUser(context, node)

  'Pull the permission out of the sitemap node as an attribute
  Dim Permission As String = node.Item("permission")
  If Not Permission Is Nothing And authorized Then
    If context.User.Identity.IsAuthenticated Then
      ' Only check permission for known users
      Return PermissionManager.HasUserPermission(context.User.Identity.Name, Permission)
    Else
      ' The presence of a permission requires a known user
      Return False
    End If
  End If

Return authorized

End Function
End
Class

The PermissionSiteMapProvider.zip file with this article includes the Provider solution and a sample web.config

Wednesday, November 23, 2005 4:27:33 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, November 10, 2005

Today I discovered that Microsoft is answering the community rant against losing web projects in VS 2005.  They have introduced Web Deployment Projects as an add-in for VS 2005.  They have essentially wrapped MS Build into a project type for the web.  Most exciting is that they did us right and took it beyond just providing a way to do exclusions and assembly naming.  My favorite new thing is the ability to do configuration management as part of the build process.  I can now have multiple configuration documents that target different deployment options.  Thank you ScottGu and the ASP.NET team!  This is exactly the kind of thing that we needed!

Thursday, November 10, 2005 10:02:31 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  | 
# Saturday, August 13, 2005

I am often asked why I have switched from using Rainbow Portal to DotNetNuke.  I promised I would answer that. 

 

Both products have matured and maintain some distinctions.  Rainbow (RB) is written in C# and DotNetNuke (DNN) is in VB.NET.  That no longer matters to me because I am not working on the core of either product.  Also, I recommend that unless you can get your code added to the core product or you are planning to support your own branch then you should avoid touching the product source code as well.  If you change a copy of the product you set yourself up for challenges with future upgrades.  Extensibility projects or modules can be written in the .NET language of your choice so I do not consider the language argument to be valid anymore.

 

The other major distinction that might be worth mentioning is localization.  From the beginning RB has supported localization and it is the only 1 of the 2 products that currently allows for easy content localization.  Both products now support localization of menus and commands fairly well.  DNN is aware of their deficiency here and they plan to address it as some point.

 

Usability is the biggest reason I now use DNN.  When I first tried version 3 I was amazed at how quickly I could add a page (tab) and fill it with modules.  I was further impressed by the content editor’s experience.  In many places where you are creating a link you get a common control that easily lets you choose between linking to a page in your site,  a file on you site or a URI offsite,.  The file dialog really won me over.  It is very common for users to want to upload a PDF or Word document and then create a link to it.  In DNN you never have to leave the link edit page to accomplish all of that.  With RB it takes several clicks and some intermediate end user training to handle the simple document upload and link creation process.  It is so painful in RB that my team typically does it for the content managers.

 

The other major reason for my switch was the community.  I was fine with RB because I know the people who created and maintain it.  However, those I work with and who manage the many websites at Idaho Commerce and Labor have no relationship with RB’s creators.  If I were to take a new job or simply wanted to find some training resources for them it begins to get ugly fast.  The DNN community is so large now that you can find just about any help you need without calling up one of the core team. 

 

Largely due to the size of the DNN community I also found a rich and active market for extensibility of the product.  Mostly I am referring to the huge number of 3rd party modules that are available at very low prices.  Often I have requests for new features on our websites and with RB I always had to replay with a number of hours it would take to custom build that functionality.  With DNN I now replay typically with a list of 3rd party modules and their purchase price.  The prices are very palatable to almost any DNN user and often you can find working solutions for free.

 

I should also mention that I have found DNN skinning to be reasonably easy and far more desirable than classic RB skinning.  I know that RB now has the ZEN skinning engine, but I am very happy with the DNN solution and skinning must remain reasonable simple for any solution to work well.  I know that RB ZEN took some magic in the page lifecycle to make it work.  DNN is much simpler in its implementation with standard .NET user controls as Skin Objects.

 

A brief note also needs to be included here about the roadmap in DNN.  Namely, they have one and they try to stick to it.

 

Personally I can see no reason to go back to RB and every reason to believe that DNN will be a great solution for some time.  It already is an amazing content management tool and application development platform.  I view DNN less as an option and more as the default foundation for nearly all sites and applications.  The best way to decide between RB and DNN is to spend a few minutes with each.  I think you will be surprised to find how much better DNN really is.

Saturday, August 13, 2005 9:06:38 AM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, July 12, 2005

Yesterday News.com gave the DotNetNuke (DNN) community a boost when it headlined an article about Microsoft and Open Source.  Congratulations Shaun and DNN for creating a product that is getting this kind of attention.  You deserve it.

If you have not yet given DNN a run then what in the world are you thinking?  DNN is no longer a hobbyists web management toy.  It has matured to a point where it can rightly be called an Enterprise Application management and hosting framework.  If you are still doing custom web sites without a tool like DNN you have more to worry about than being outsourced to India.

Tuesday, July 12, 2005 7:22:45 AM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, June 01, 2005

Today I had a question from one of our developers about bridging ASP and ASP.NET applications and how to handle the security.  What I always recommend for all .NET web applications is that people use what is built-in.  Forms authentication works very well, already has security for things like authentication tickets, and it already handles checking authorization by just adding the <authorization> tag in web.config. 

Many ASP applications already have the concept of a userID and Roles.  In February I presented a session at VS Live that included some sample code that will easily address setting up forms authentication with roles for ASP.NET.  Lets jump right into it in a basic walk-through.

#1 Web.config settings.  These basic settings tell the framework that all unknown users should be redirected to the Logon.aspx page.  If the users are known (authenticated) then they can browse the other pages in the application.

<authentication mode="Forms">
<forms loginUrl="Logon.aspx" name="myAuthCookie" timeout="60" path= "/"></forms>
</authentication>

<authorization>
<deny users="?" />
<allow users="*" /> <!-- Allow all users -->
</authorization>

#2 Logon.aspx.  We could have a username and password input form or any kind of input we want to use to validate the user.  Since I am talking about a solution here that makes it easy to share authentication between classic ASP and ASP.NET we will leave the ASPX page itself blank and handle the check in code.  My recommendation is that the ASP classic application would instead trust the .NET authentication.  For that you might simply set an extra cookie from .NET that the ASP classic could read.  It really depends on how your ASP application validates users.  You would simply want to have the ASP.NET code duplicate the current ASP mechanism so when the user hits ASP pages they do not know any difference.  In other words, the ASP code would still check for a specific cookie or something like that. 

#2a Logon.aspx.cs.  In the code behind the for the authentication page is where all the real work happens.  Lets look at it in detail for the given scenario.

private void Page_Load(object sender, System.EventArgs e)
{
string userID = "";

// Check the ASP classic authentication cookie array or whatever you use in ASP to know the user is logged in
if (!(Request.Cookies("MyApplication") == null))
{
// Assuming here if authenticated then we also have a userID
userID = Request.Cookies("MyApplication").Values.Get("userID")
}

if (userID == "")
{
// unknown user. Send them back to classic ASP login page
Response.Redirect("login.asp");
}
else
{
// We have a userID from the ASP application. Use it to create an authentication ticket
// Get a pipe delimited string of the groups (Roles) that the user belongs to.
// Typical ASP applications store a list of roles in a database table. Hard coded here for sample.
string groups = "Editors|Registered Users|Newsletters";

// Create a forms authentication ticket that we can stuff the userID and Roles into
FormsAuthenticationTicket authTicket =
new FormsAuthenticationTicket(1,
userID,
DateTime.Now,
DateTime.Now.AddMinutes(60),
false, groups);
// Encrypt the ticket.
String encryptedTicket = FormsAuthentication.Encrypt(authTicket);

// Stuff the ticket into a cookie
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket);
Response.Cookies.Add(authCookie);

// Return the user to the requested page now that an authentication ticket is created
Response.Redirect(FormsAuthentication.GetRedirectUrl(userID, false));
}
}

#3 Global.asax.cs.  This is where the magic of .NET forms authentication really happens.  If you are not stuffing the roles into the authentication ticket then much of this whole process can be greatly simplified.  Putting the Roles into the ticket will give us an easy way to apply them to the current user on each request.  Lets take a look.

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];

if(null == authCookie)
{
// There is no authentication cookie.
return;
}

FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch(Exception ex)
{
// Log exception details (omitted for simplicity)
return;
}

if (null == authTicket)
{
// Cookie failed to decrypt.
return;
}

// When the ticket was created, the UserData property was assigned a
// pipe delimited string of group names.
String[] groups = authTicket.UserData.Split(new char[]{'|'});

// Create an Identity object
GenericIdentity id = new GenericIdentity(authTicket.Name, "TrustedASPAuthentication");

// This principal will flow throughout the request.
GenericPrincipal principal = new GenericPrincipal(id, groups);

// Attach the new principal object to the current HttpContext object
Context.User = principal;
}


That is really about all there is to it.  By using the built-in authentication and authorization tools in ASP.NET and stuffing the roles into the ticket and then the current principal your code can leverage the built-in methods for checking Role membership. 

User.IsInRole("Editors") would return true for us since "Editors" is one of the roles I hard coded the user to be in.

I hope that someone finds this brief article helpful as an introduction into using Forms authentication and Role authorization in ASP.NET.

Wednesday, June 01, 2005 11:46:11 AM (Mountain Daylight Time, UTC-06:00)  #    Disclaimer  |  Comments [5]  |