Ensure an SPUser Exists in an Application Page Within an Anonymously Accessible Site

I was recently confronted with a requirement to develop an application page that could retrieve information about the currently logged in user from an external system based on that user’s email address. Piece of cake, I thought: I would just use the SPContext.Current.Web.CurrentUser object, which contains a string property called Email containing that SPUser‘s email address. I would take that string, pass it to the external system, get my information back, and call it a day. There was only one problem: the application page had to run within a site that had anonymous access enabled, and none of the users who would be accessing the page were explicitly assigned any permissions within the site.

A quick aside about anonymously accessible application pages

The default “Application Page” SPI within Visual Studio 2010 creates a page that inherits from the LayoutsPageBase class. A frequent complaint I hear is that an application page is prompting a user to log in even though that page is being accessed from within the context of an anonymously accessible site. If I had wanted my application page to be anonymously accessible, I would have to make the following tweaks to what Visual Studio 2010 gives me by default:

  • First, ensure that anonymous access is enabled for the Entire Web Site. I would do this from Site Actions > Site Permissions in SharePoint.
  • Change the page inheritance so that it inherits from the UnsecuredLayoutsPageBase class (shown below).
  • Explicitly override the AllowAnonymousAccess property and set its value to true (shown below).

But back to the problem at hand…

Since I want to force users to log in to access my application page (even with anonymous access enabled at the site level), I will leave the default base class of LayoutsPageBase. Can anyone see what problem might arise with my scenario?

It turns out that since none of the users accessing the page were explicitly assigned any permissions within the site (nor belonged to any site groups), SPContext.Current.Web.CurrentUser was returning null!

Fortunately, there is some good news here: even though the SPContext may not know who the current user is, the System.Web.HttpContext does! The property this.Context.User.Identity.Name contains the login name of the current user, which I can then pass to the SPWeb.EnsureUser() function to create the SPUser in the given SPWeb based on that login name. The EnsureUser() function returns an SPUser object that is populated with the email address of the user (along with the other properties of the SPUser object such as LoginName, Name, ID,
etc.)

NOTE: Do NOT try to call SPWeb.EnsureUser() in the context of the currently logged in user. It is important to run this code inside a RunWithElevatedPrivileges() delegate because the System Account will have the necessary permissions to add the specified user to the SPWeb. After all, if the specified user already had permission to do this, we wouldn’t need to be adding him or her in the first place!

The final code to accomplish everything (with proper error checking and handling omitted for brevity) looks like this:

Note that I don’t wrap the entire method with a RunWithElevatedPrivileges() delegate or web.CurrentUser would return the identity of the System Account, which is definitely not what I want.

Has anyone else encountered a similar scenario and solved the problem differently? Please feel free to discuss in the comments below!