OpenIdPortableArea is a pluggable component that enables support for OpenId. This post is a short walkthrough for implementing OpenId in a sample application.
Step 1: Ducks all in a row
First, download and unzip the latest release (0.1.0.3 as of today, found at http://openidportablearea.codeplex.com/).

Open Visual Studio and create a new ASP.NET MVC 2 project. In the Solution Explorer, right click on the project and go to Add, New Folder and call it “lib” (the name doesn’t really matter, just what I usually use). Copy all of the .dll and .xml files from the files you extracted, and paste them into the new folder in your project. Then, add a reference for DotNetOpenAuth.dll, MvcContrib.dll, and OpenIdPortableArea.dll.

Next, create an Areas folder in your solution and copy the Views\Web.config into it. This is important because without it, you’ll get a YSOD. If you forget this and try to run the application it will throw a clearly worded exception during app start.

Up to this point, the portable area gets hooked up automatically as long as your Application_Start calls AreaRegistration.RegisterAllAreas().
Step 2: The Old Switcheroo
If you run the application, you’ll notice that the [ Log On ] link still sends you to ~/Account/LogOn.
The OpenIdPortableArea comes with its own login status widget. To substitute that behavior, open Site.master and add <%@ Import Namespace="OpenIdPortableArea.UI" %> to the top of the page, and then add <%= Html.LoginStatusWidget() %> where the RenderPartial(“LogonUserControl”) was.
Now, when you run the application you should be able to click that link and go to ~/OpenId.
Oh yeah! Don’t forget to change your web.config to <forms loginUrl="~/OpenId" timeout="2880"/>. That way, when you use [Authorize] it will redirect to the correct Url.
Step 3: Know Thy User
If you enter an OpenId and click “Login” you will be sent to that OpenId provider to authenticate with them. The provider then redirects you back to the site, and you arrive on ~/OpenId/Success. If you were already authenticated with the provider, they might have automatically redirected you (i.e. you may have gone from the Login page to the Success page).
When the provider sends the user back to the site, the portable area sends an AuthenticatedMessage through MvcContrib’s Bus. It contains information about the authenticated user, and allows you the opportunity to handle it explicitly. To handle this message, create a class that inherits from MessageHandler<AuthenticatedMessage>. In this class, override the Handle() method. This is where you can employ your own domain to associate your user with their OpenId. There are two simple scenarios to code for. You already know the user, or you don’t.
using System;
using System.Web;
using MvcContrib.PortableAreas;
using OpenIdPortableArea.Helpers;
using OpenIdPortableArea.Messages;
using SampleApplication.Models;
namespace SampleApplication.Services.PortableAreaMessageHandlers
{
public class AuthenticatedMessageHandler
: MessageHandler<AuthenticatedMessage>
{
UserRepository _userRepository = new UserRepository();
public override void Handle(AuthenticatedMessage message)
{
User user = _userRepository
.FindByOpenId(message.ClaimedIdentifier);
if (user != null)
{
OpenIdHelpers.Login(user.Name,
user.Email,
new TimeSpan(0, 5, 0),
true);
}
else
{
user = new User
{
OpenId = message.ClaimedIdentifier,
Email = message.ClaimsResponse.Email,
Name = message.ClaimsResponse.FullName
};
HttpContext.Current.Session.Add(
"user",
user);
message.ReturnUrl = "~/Home/Register";
}
}
}
}
In this example, I use a UserRepository* to try and find a user by the ClaimedIdentifier (their OpenId). If a user is found, the OpenIdHelpers class provides a simpler call for issuing a FormsAuthenticationTicket either by passing an instance of FormsAuthenticationTicket, or passing a few parameters. If a user wasn’t found, a new one gets created and stuffed into session until the user gets redirected. The ReturnUrl is also set to ~/Home/Register. More on that later (Step 4).
*By the way, I cheated and hard coded this to return a user with my own OpenId. In reality, your UserRepository will really be able to query the database for a user with the matching OpenId.
Notice the message has a ClaimsResponse property containing information about the user. Yup, you have (some) control over that, as well. It, too, is a message waiting to be handled. This time, it’s a ClaimsRequestMessage. Here’s an example:
using OpenIdPortableArea.Messages;
using MvcContrib.PortableAreas;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
namespace SampleApplication.Services.PortableAreaMessageHandlers
{
public class ClaimsRequestMessageHandler
: MessageHandler<ClaimsRequestMessage>
{
public override void Handle(ClaimsRequestMessage message)
{
message.Claim.Email = DemandLevel.Require;
message.Claim.FullName = DemandLevel.Require;
}
}
}
It's pretty straight forward. Set each member to the desired DemandLevel. Now, whenever the OpenId Provider redirects the authenticated user back to the site, hopefully these demands will be honored and you can pre-populate your new user’s information.
In order to make ClaimsRequests work, you must also add the following settings to your ~/Web.config (otherwise message.Claim will be null):
<configuration>
<configSections>
<section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true"/>
</configSections>
<dotNetOpenAuth>
<openid>
<relyingParty>
<behaviors>
<!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible
with OPs that use Attribute Exchange (in various formats). -->
<add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" />
</behaviors>
</relyingParty>
</openid>
</dotNetOpenAuth>
</configuration>
*If you don’t care about the ClaimsRequest, and aren’t using ClaimsResponse in the message handler for AuthenticatedMessage, then this setting shouldn’t matter.
WAIT! Creating the message handlers is great, but the MvcContrib.Bus needs to know about these classes. Add the following lines to Application_Start() in Global.asax.cs:
MvcContrib.Bus.AddMessageHandler(typeof(AuthenticatedMessageHandler));
MvcContrib.Bus.AddMessageHandler(typeof(ClaimsRequestMessageHandler));
Step 4: Paperwork

In the future, I may try to alleviate this implementation burden in OpenIdPortableArea. But for now, you’ve got to do it (cheers!). Relax, it isn’t so bad.
Create a Register() method on your HomeController, and create a view for it. Make the view strongly typed for (at least) your User. Then, use the templated helper Html.EditorForModel() to get the form on the page.
HomeController:
private UserRepository _userRepository = new UserRepository();
[HttpGet]
public ActionResult Register()
{
User user = (User)Session["user"];
return View(user);
}
[HttpPost]
public ActionResult Register(User user)
{
bool exists = _userRepository.FindAll().Any(u => u.Name == user.Name);
if (exists)
{
ModelState.AddModelError("", "User already exists with that name.");
return View(user);
}
if (ModelState.IsValid)
{
_userRepository.Add(user);
_userRepository.Save();
// Issue forms authentication ticket.
OpenIdHelpers.Login(user.Name, string.Empty, new TimeSpan(5, 0, 0, 0), false);
return RedirectToAction("Index", "Home");
}
else
{
return View(user);
}
}
Views/Home/Register.aspx:
The idea is pretty simple. Show the user their information (filling in as much as possible via ClaimsResponse), and they click the Register button. On the POST, perform validation on the user. If everything checks out, add the user to the repository and authenticate them. Lastly, redirect to wherever you please (or… ~/Home/Index).
Summary
Now, if you run your sample application you should be able to login with OpenId freely.
The [ Logout ] link at the top points to ~/OpenId/Logout. By default, the portable area will handle this nicely for you. However, if you wish to intervene there are two messages LoggingOutMessage and LoggedOutMessage. They’ll be a topic for another post.
Here’s the short To Do list for implementing this portable area:
- Add Files & References
- Add LoginStatusWidget to MasterPage
- Change web.config forms loginurl to ~/OpenId
- Create a MessageHandler<> for AuthenticatedMessage
- Add the message handler to MvcContrib.Bus
- Add User Registration functionality