Consuming OpenIdPortableArea

This documentation outlines how to consume OpenIdPortableArea in your ASP.NET MVC 2 application.

Dependencies

Using OpenIdPortableArea requires that your project have 3 dependencies:
  • DotNetOpenAuth.dll
  • MvcContrib.dll
  • OpenIdPortableArea.dll

Add references to these DLLs to your project.

Enabling Portable Areas with MvcContrib

To enable Portable Areas in your project, you need only call AreaRegistration.RegisterAllAreas(); inside of Global.asax.

Set the LoginUrl in Web.config

Using the [Authorize] attribute in your application causes the user to be redirected to the login page. To make this work with OpenIdPortableArea, change the following setting in Web.config:

<authentication mode="Forms">
  <forms loginUrl="~/OpenId" timeout="2880"/>
</authentication>

Handling Messages

MvcContrib Portable Areas use a Bus mechanism for communicating messages between the consuming application and the portable area. OpenIdPortableArea provides some basic messages that can be handled.
  • ClaimsRequestMessage - this message allows the observer to define what fields are to be requested from the OpenId provider. Interacting with this message uses DotNetOpenAuth's ClaimRequest class. Ergo, using this message requires a reference to that DLL.
  • AuthenticatedMessage - contains a majority of the logic for handling OpenId logins.
  • UnauthenticatedMessage - this message merely indicates that the user either canceled or failed to be authenticated. This only contains a Message property that describes what happened.
  • LoggingOutMessage - this message indicates that the user is logging out.
  • LoggedOutMessage - this message indicates that the user has finished logging out

ClaimsRequestMessage

The consuming application must define a class that is capable of handling the ClaimsRequestMessage. A class is capable when it implements MessageHandler<ClaimsRequestMessage>. The following code is an example of how to handle the message:

using OpenIdPortableArea.Messages;
using MvcContrib.PortableAreas;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;

namespace MvcApplication1.Services.PortableAreaHandlers
{
    public class ClaimsRequestMessageHandler : MessageHandler<ClaimsRequestMessage>
    {
        public override void Handle(ClaimsRequestMessage message)
        {
            message.Claim.Email = DemandLevel.Require;
        }
    }
}

Then, the consuming application needs to configure the MvcContrib Bus to handle messages with this class. This is handled in Global.asax.cs during Application_Start:

MvcContrib.Bus.AddMessageHandler(typeof(ClaimsRequestMessageHandler));

About Setting Demands on Claims Requests
The ClaimsRequestMessage is purely optional, and is not necessary to make OpenId work. It provides an opportunity for the consuming application to set demand levels for user information. The catch is that OpenId Providers are not required to honor these demands.

"DotNetOpenAuth offers “behaviors”, which automatically modify outgoing or incoming OpenID messages in a standard way. One built-in behavior is the AXFetchAsSregTransform behavior, which for the relying party automatically translates requests that include the Simple Registration extension into a request that includes that extension and/or any of three formats of Attribute Exchange extensions depending on the Provider you’re authenticating the user with. By activating this behavior in your site, all you have to do is use ClaimsRequest and ClaimsRespones, and you’ll have a very high chance of getting the attributes you’re requesting if the Provider supports attributes at all." - The AXFetchAsSregTransform Behavior
To enable AXFetchAsSregTransform Behavior, add the following configuration in the web.config (from http://dotnetopenauth.net):

<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>

AuthenticatedMessage

This message indicates that the user was successfully authenticated by an OpenId provider. To handle this message, the consuming application must define a class that implements MessageHandler<AuthenticatedMessage>.

using System;
using System.Web;
using MvcContrib.PortableAreas;
using OpenIdPortableArea.Messages;
using OpenIdPortableArea.Helpers;

namespace MvcApplication1.Services.PortableAreaHandlers
{
    public class AuthenticatedMessageHandler : MessageHandler<AuthenticatedMessage>
    {
        public override void Handle(AuthenticatedMessage message)
        {
            const string openid = "http://johncoder.myopenid.com/";

            if (message.ClaimedIdentifier == openid)
            {
                OpenIdHelpers.Login("John Nelson", "jnelson@johncoder.com", new TimeSpan(0, 5, 0), true);
            }
            else
            {
                HttpContext.Current.Session.Add("OpenIDClaimedIdentifier", message.ClaimedIdentifier);
                message.ReturnUrl = "~/Home/Register";
            }
        }
    }
}

For this message, there are two basic scenarios to handle.
  1. The authenticated user is known, and they need to be issued an authentication cookie.
  2. The authenticated user is unknown, and the consuming application can set the ReturnUrl to a registration page. This is also the only opportunity for the consumering application to access information about the User.

To enable this message, add the following line in Global.asax.cs in Application_Start:

MvcContrib.Bus.AddMessageHandler(typeof(AuthenticatedMessageHandler));

OpenIdPortableArea Widgets

The project provides a few partial views for your disposal. Those widgets are:
  • LoginStatusWidget - displays the status of the current user (authenticated or unauthenticated).
  • LoginWidget - adds a form to the view that posts back to the OpenIdController Login Action.
  • ProvidersWidget - adds some default OpenId providers to the view:
    • Google
    • Yahoo
    • myopenid
    • AOL
These widgets are accessible via HtmlHelper extensions. To add these extensions to your views, add an Import statement to the top of the page:

<%@ Import Namespace="OpenIdPortableArea.UI" %>

Or, add the namespace import to Web.config:

<namespaces>
    <add namespace="System.Web.Mvc"/>
    <add namespace="System.Web.Mvc.Ajax"/>
    <add namespace="System.Web.Mvc.Html"/>
    <add namespace="System.Web.Routing"/>
    <add namespace="System.Linq"/>
    <add namespace="System.Collections.Generic"/>
    <add namespace="OpenIdPortableArea.UI"/>
</namespaces>

After the namespace is available, the HtmlHelper allows you to add the respective widgets via:

<%= Html.LoginStatusWidget() %>
<%= Html.LoginWidget() %>
<%= Html.ProvidersWidget() %>

It is recommended that the LoginStatusWidget be added to the MasterPage.

Overriding Views in OpenIdPortableArea

Any of the views in OpenIdPortableArea can be overridden. MvcContrib's Embedded ViewEngine builds on the existing ViewEngine by adding a last resort location for views. It sounds confusing, but it really isn't. By default, ASP.NET MVC will use conventions to resolve a view. It looks in Areas/AreaName/Views/ControllerName for a .aspx or .ascx file with the name of the view, and moves on to Views/Shared if it can't find it. The MvcContrib ViewEngine adds embedded locations as a final check.

That means that the consuming application can define a Areas/OpenId/Views/OpenId/Login.aspx file of its own, and override OpenIdPortableArea's provided Login view by convention. The Widgets may be overridden in the same location, or in Views/Shared.

When overriding the LoginWidget, be sure that the form specifies area = "OpenId" so it posts back to the correct location:

Html.BeginRouteForm(new { action = "Login", controller = "OpenId", area = "OpenId" })

You may wish to override the ProvidersWidget and supply your own recommended OpenId Providers.

It may also be the case that your application has an incompatible MasterPage. When this happens, the Login.aspx page should be overridden. The embedded Login.aspx page uses the LoginWidget and ProvidersWidget, so as long as you include those no functionality will be lost. Remember to include <%= Html.ValidationSummary() %> as well

Helpers

The OpenIdPortableArea.Helpers namespace provides a static class with a helper method for issuing authentication cookies via Forms Authentication. You may create your own FormsAuthenticationTicket:

FormsAuthenticationTicket ticket =
                new FormsAuthenticationTicket
                        (1, name,
                        dateTime,
                        dateTime.Add(duration),
                        isPersistant,
                        userData);
OpenIdHelpers.Login(ticket, true);

Or, pass a few parameters to an overloaded method signature:

OpenIdHelpers.Login("John Nelson", "jnelson@johncoder.com", new TimeSpan(0, 5, 0), true);

Last edited Apr 28, 2010 at 1:54 AM by rockon1119, version 12

Comments

No comments yet.